?
Solved

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

Posted on 2013-01-25
3
Medium Priority
?
923 Views
Last Modified: 2016-10-14
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.
0
Comment
Question by:cnxmax
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
3 Comments
 
LVL 4

Accepted Solution

by:
mcmahon_s earned 2000 total points
ID: 38823480
Well, the good news is you are right. Multiple occurrences of "item" is what is causing the issue. The bad news is there is no easy way out of this. You either need to transform you xml so the one of the item tags are renamed or process the xml more manually i.e. reading one element at a time and loading it into your class.
0
 
LVL 16

Expert Comment

by:hjgode
ID: 38823599
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

0
 

Author Closing Comment

by:cnxmax
ID: 38931282
Changed second occurance of item to BindingItem ... all is well.
0

Featured Post

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

For most people, the WrapPanel seems like a magic when they switch from WinForms to WPF. Most of us will think that the code that is used to write a control like that would be difficult. However, most of the work is done by the WPF engine, and the W…
This article shows how to deploy dynamic backgrounds to computers depending on the aspect ratio of display
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
Michael from AdRem Software outlines event notifications and Automatic Corrective Actions in network monitoring. Automatic Corrective Actions are scripts, which can automatically run upon discovery of a certain undesirable condition in your network.…

770 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question