Link to home
Start Free TrialLog in
Avatar of cnxmax
cnxmax

asked on

Deserialize XML string that contains an array of "an array of elements"

I am using vs 2012, vb.net, .net 4.0 and I need to deserialize the following XML:

<?xml version="1.0" encoding="UTF-8"?>
-<data>
      -<apps>
            -<item>
                  <name>xxx</name>
                  <folder>c:\web\xxx</folder>
                  <version>2.xxx</version>
                  -<bindings>
                        <item>www.example.com</item>
                        <item>beta.xxx.com</item>
                  </bindings>
            </item>
            -<item>
                  <name>xxx</name>
                  <folder>c:\web\xxx</folder>
                  <version>3.xxx</version>
                  -<bindings>
                        <item>www.example.com</item>
                        <item>xxx:*</item>
                  </bindings>
            </item>
            -<item>
                  <name>xxx</name>
                  <folder>c:\web\xxx</folder>
                  <version>3.xxx</version>
                  -<bindings>
                        <item>*</item>
                  </bindings>
            </item>
      </apps>
</data>

I created the following structure (in Class Pong):


    Public Structure data
        <XmlArray>
        <XmlArrayItem("item")>
        Public apps As List(Of dataItem)
    End Structure

    Public Structure dataItem
        Public name As String
        Public folder As String
        Public version As String
        <XmlArray>
        <XmlArrayItem("item")>
        Public bindings As List(Of bindingItem)
    End Structure

    Public Structure bindingItem
        Public item As String
    End Structure



I use the following to deserialize the XML into the Pong.data structure:

     Dim serializer As New XmlSerializer(GetType(Distribute.data))
     Dim settings As New XmlReaderSettings()

     Using textReader As New StringReader(sXML)
         Using xmlReader__1 As XmlReader = XmlReader.Create(textReader, settings)
             oMasterData = DirectCast(serializer.Deserialize(xmlReader__1), Distribute.data)
         End Using
     End Using


I get the correct array of information in oMasterData for the various "item" array (name, folder and version) but
the inner array for bindings comes back with a length=0.

I found many examples of similar deserializations but each falls short when
the main structure is an array and in the elements of that array another array occurs.

I had a suspision that a problems occurs when both arrays are called "item".

Any code examples (either vb or C#, C++) would be appreciated.
ASKER CERTIFIED SOLUTION
Avatar of mcmahon_s
mcmahon_s
Flag of Australia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of hjgode
Hello

I have done that in the past using Class definitions with arrays.

The important snippets are:

        [XmlRootAttribute("Subsystem")]
        public class SubSystem{
            [XmlElement("Group")]
            public Group[] groups;
            [XmlAttribute("Name")]
            public string sName{get; set;}

Open in new window


You see that groups is an array of Group objects. Then the Group class again references another array:

        public class Group{
            [XmlElement("Field")]    
            public Field[] fields;
            [XmlAttribute("Name")]
            public string sName{get; set;}
            public void Display(){
                Console.WriteLine(sName);
                foreach(Field f in fields){
                    f.Display();
                }
            }
            private string _shortName;
            public string shortName{
                get{return _shortName;}
                set{string[] s=value.Split(new char[]{'\\'});
                    _shortName=s[s.Length-1];
                }
            }
        }

        public class Field{
            private string _shortName;
            public string sShortName{
                get{return _shortName;}
            }
       ...

Open in new window


So, we can have one or more Group TAGs inside a subsystem TAG and that can contain one more Field TAGs.

The xml has groups and therein you can have additional (sub) groups and fields. Here is an excerpt of an xml:

<?xml version="1.0" encoding="UTF-8"?>
	<DevInfo Action="Set" Persist="true" SeqNo="165">
	<!-- SETTINGS -->
	    <Subsystem Name="Data Collection">
	        <Group Name="Scanners" Instance="0">
	            <Group Name="Symbologies">
	                <Group Name="Code 39">
	                    <Field Name="Enable Code 39">1</Field>
	                    <Group Name="Options">
	                        <Field Name="Full ASCII Conversion">0</Field>
	                        <Field Name="Start/Stop transmission">0</Field>
	                        <Field Name="Start character">2</Field>
	                        <Field Name="Verify check digit">0</Field>
	                        <Field Name="Transmit check digit">0</Field>
	                        <Field Name="Reading range">1</Field>
	                        <Field Name="Reading tolerance">0</Field>
	                        <Field Name="Length mode">0</Field>
	                        <Field Name="Length 1">3</Field>
	                        <Field Name="Length 2">3</Field>
	                        <Field Name="Length 3">3</Field>
	                        <Field Name="User defined symbology ID">B1</Field>
	                    </Group>
	                </Group>
	                <Group Name="UPC/EAN">
	                    <Field Name="Enable UPC A">1</Field>
	                    <Field Name="Enable UPC E">1</Field>
	                    <Field Name="Enable EAN 8">1</Field>
	                    <Field Name="Enable EAN 13">1</Field>
	                    <Field Name="Enable UPC-E1">0</Field>
          ...

Open in new window


Here is the code I sue to serialze/deserialize such xml's:
    using System;
    using System.IO;
    using System.Xml;
    using System.Xml.Serialization;
    using System.Collections;
    using System.Collections.Generic;

    // based on http://drdobbs.com/windows/184416669?pgno=2

    namespace ITCDeviceMonitor
    {
        [XmlRootAttribute("Subsystem")]
        public class SubSystem{
            [XmlElement("Group")]
            public Group[] groups;
            [XmlAttribute("Name")]
            public string sName{get; set;}
            
           public void Display() // 
            {
                Console.WriteLine(sName);
                foreach (Group g in groups)
                {
                    g.Display();
                }
            }
            public static SubSystem deserialize(string sXMLfile){
                XmlSerializer xs = new XmlSerializer(typeof(SubSystem));
                //StreamReader sr = new StreamReader("./SystemHealth.xml");
                StreamReader sr = new StreamReader(sXMLfile);
                SubSystem s = (SubSystem)xs.Deserialize(sr);
                sr.Close();
                return s;
            }
            public static void serialize(SubSystem subsys, string sXMLfile){
                XmlSerializer xs = new XmlSerializer(typeof(SubSystem));
                //omit xmlns:xsi from xml output
                //Create our own namespaces for the output
                XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
                //Add an empty namespace and empty value
                ns.Add("", "");
                //StreamWriter sw = new StreamWriter("./SystemHealth.out.xml");
                StreamWriter sw = new StreamWriter(sXMLfile);
                xs.Serialize(sw, subsys, ns);
            }
        }
        
        public class Group{
            [XmlElement("Field")]    
            public Field[] fields;
            [XmlAttribute("Name")]
            public string sName{get; set;}
            public void Display(){
                Console.WriteLine(sName);
                foreach(Field f in fields){
                    f.Display();
                }
            }
            private string _shortName;
            public string shortName{
                get{return _shortName;}
                set{string[] s=value.Split(new char[]{'\\'});
                    _shortName=s[s.Length-1];
                }
            }
        }
        
        public class Field{
            private string _shortName;
            public string sShortName{
                get{return _shortName;}
            }
            private string _sName;
            [XmlAttribute("Name")]
            public string sName{
                get{return _sName;} 
                set{
                    _sName=value;
                    string[] s = value.Split(new char[]{'\\'});
                    _shortName=s[s.Length-1];
                }
            }
            
            [XmlAttribute("Type")]
            public string sType{get; set;}
            [XmlText(typeof(string))]
            public string sValue{get; set;}
            
            // This field shouldn't be serialized 
               // if it is uninitialized.
            //[XmlAttribute("Error")]
            public string sError{get;set;}
            
            public void Display(){
                Console.WriteLine(_shortName + " " + sType + " "  + sValue + " ");
            }
        }
    }

Open in new window

Avatar of cnxmax
cnxmax

ASKER

Changed second occurance of item to BindingItem ... all is well.