Xerc
Xerc

Reputation: 155

XML to Classes via LINQ with nested lists

So I am trying to parse out this xml code below using linq into classes and I am having a hard time getting the inner lists. The xml is contained in an XDocument that I am parsing through.

<Response>
<ResponseTransaction>
    <transaction>
        <tranId>3216</tranId>
        <tranName>TestTran01</tranName>
        <tranResultList>
            <tranResult>
                <state>Expired</state>
                <created>2019-02-23</created>
                <docList>
                    <doc id="1" name="Doc1" />
                    <doc id="2" name="Doc2" />
                    <doc id="3" name="Doc3" />
                    <doc id="4" name="Doc4" />
                </docList>
                <roleList>
                    <role role="usrRole" id="1">
                        <firstName>Jack</firstName>
                        <lastName>Daniels</lastName>
                        <email>[email protected]</email>
                        <docList>
                            <doc id="1" name="Doc1" status="removed" />
                            <doc id="2" name="Doc2" status="current" />
                            <doc id="3" name="Doc3" status="current" />
                            <doc id="4" name="Doc4" status="current" />
                        </docList>
                    </role>
                </roleList>
            </tranResult>
            <tranResult>
                <state>undefined</state>
                <created>2019-02-24</created>
                <docList>
                    <doc id="1" name="Doc1" />
                    <doc id="2" name="Doc2" />
                    <doc id="3" name="Doc3" />
                    <doc id="4" name="Doc4" />
                </docList>
                <roleList>
                    <role role="usrRole" id="1">
                        <firstName>Jack</firstName>
                        <lastName>Daniels</lastName>
                        <email>[email protected]</email>
                        <docList>
                            <doc id="1" name="Doc1" status="removed" />
                            <doc id="2" name="Doc2" status="current" />
                            <doc id="3" name="Doc3" status="current" />
                            <doc id="4" name="Doc4" status="current" />
                        </docList>
                    </role>
                </roleList>
            </tranResult>
            <tranResult>
                <state>Current</state>
                <created>2019-02-25</created>
                <docList>
                    <doc id="1" name="Doc1" />
                    <doc id="2" name="Doc2" />
                    <doc id="3" name="Doc3" />
                    <doc id="4" name="Doc4" />
                </docList>
                <roleList>
                    <role role="usrRole" id="1">
                        <firstName>Jack</firstName>
                        <lastName>Daniels</lastName>
                        <email>[email protected]</email>
                        <docList>
                            <doc id="1" name="Doc1" status="removed" />
                            <doc id="2" name="Doc2" status="current" />
                            <doc id="3" name="Doc3" status="current" />
                            <doc id="4" name="Doc4" status="current" />
                        </docList>
                    </role>
                </roleList>
            </tranResult>
        </tranResultList>
     </transaction>
   </ResponseTransaction>
</Response>

Here are the classes I am working with.

   public class Transaction {
        public string TranId { get; set; }
        public string TranName { get; set; }
        public List<TranResult> TranResultList { get; set; }
}

public class TranResult {
        public string State { get; set; }
        public string DateCreated { get; set; }
        public List<Document> DocumentList { get; set; }
        public List<Role> RoleList { get; set; }
}

public class Document {
        public string Id { get; set; }
        public string Name { get; set; }
        public string status { get; set; }
}

public class Role {
        public string Id { get; set; }
        public string RoleName { get; set; }
        public User user { get; set; }
}

public class User {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public List<Document> DocumentList { get; set; }
}

If there were no inner lists in this XML document then it would be simple and I could do just a simple LINQ query and be done. I am not sure I I can loop through the inner lists through.

Any help is appreciated.

Upvotes: 0

Views: 62

Answers (3)

jdweng
jdweng

Reputation: 34421

There were three issues

  1. The serializer is case sensitive. Since the capitialization of your class names and the xml tag names didn't match you need to add attributes in square brackets above the properties in the classes
  2. When you have arrays in serialization you can do one of two things. First you can have a class for each tag in the xml. Or you can combine two tags into one class using XmlArray and XmlArrayItem. When having a class for each xml tag use XmlElement along with a List object or an array object. Without XmlElement the Net Library assumes two classes (XmlArray and XmlArrayItem).
  3. You were missing two classes for the first two tags in the xml file.

See code below

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace ConsoleApplication1
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            XmlReader reader = XmlReader.Create(FILENAME);
            XmlSerializer serializer = new XmlSerializer(typeof(Response));
            Response response = (Response)serializer.Deserialize(reader);

        }
    }
    [XmlRoot("Response")]
    public class Response
    {
        [XmlElement("ResponseTransaction")]
        public ResponseTransaction responseTransaction { get; set; }
    }
    [XmlRoot("ResponseTransaction")]
    public class ResponseTransaction
    {
        [XmlElement("transaction")]
        public Transaction transaction { get; set; }
    }
    [XmlRoot("transaction")]
    public class Transaction
    {
        [XmlElement("tranId")]
        public string TranId { get; set; }
        [XmlElement("tranName")]
        public string TranName { get; set; }
        [XmlArray("tranResultList")]
        [XmlArrayItem("tranResult")]
        public List<TranResult> TranResultList { get; set; }
    }

    public class TranResult
    {
        [XmlElement("state")]
        public string State { get; set; }
        [XmlElement("created")]
        public string DateCreated { get; set; }
        [XmlArray("docList")]
        [XmlArrayItem("doc")]
        public List<Document> DocumentList { get; set; }
        [XmlArray("roleList")]
        [XmlArrayItem("role")]
        public List<Role> RoleList { get; set; }
    }

    public class Document
    {
        [XmlAttribute("id")]
        public string Id { get; set; }
        [XmlAttribute("name")]
        public string Name { get; set; }
        [XmlAttribute("status")]
        public string status { get; set; }
    }

    public class Role
    {
        [XmlAttribute("id")]
        public string Id { get; set; }
        [XmlAttribute("role")]
        public string RoleName { get; set; }
        [XmlElement("firstName")]
        public string FirstName { get; set; }
        [XmlElement("lastName")]
        public string LastName { get; set; }
        [XmlElement("email")]
        public string Email { get; set; }
        [XmlArray("docList")]
        [XmlArrayItem("doc")]
        public List<Document> DocumentList { get; set; }
    }
}

Added a Class User


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace ConsoleApplication1
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            XmlReader reader = XmlReader.Create(FILENAME);
            XmlSerializer serializer = new XmlSerializer(typeof(Response));
            Response response = (Response)serializer.Deserialize(reader);

        }
    }
    [XmlRoot("Response")]
    public class Response
    {
        [XmlElement("ResponseTransaction")]
        public ResponseTransaction responseTransaction { get; set; }
    }
    [XmlRoot("ResponseTransaction")]
    public class ResponseTransaction
    {
        [XmlElement("transaction")]
        public Transaction transaction { get; set; }
    }
    [XmlRoot("transaction")]
    public class Transaction
    {
        [XmlElement("tranId")]
        public string TranId { get; set; }
        [XmlElement("tranName")]
        public string TranName { get; set; }
        [XmlArray("tranResultList")]
        [XmlArrayItem("tranResult")]
        public List<TranResult> TranResultList { get; set; }
    }

    public class TranResult
    {
        [XmlElement("state")]
        public string State { get; set; }
        [XmlElement("created")]
        public string DateCreated { get; set; }
        [XmlArray("docList")]
        [XmlArrayItem("doc")]
        public List<Document> DocumentList { get; set; }
        [XmlArray("roleList")]
        [XmlArrayItem("role")]
        public List<Role> RoleList { get; set; }
    }

    public class Document
    {
        [XmlAttribute("id")]
        public string Id { get; set; }
        [XmlAttribute("name")]
        public string Name { get; set; }
        [XmlAttribute("status")]
        public string status { get; set; }
    }

    public class Role
    {
        private User user = new User();
        [XmlAttribute("id")]
        public string Id { get; set; }
        [XmlAttribute("role")]
        public string RoleName { get; set; }

        [XmlElement("firstName")]
        public string FirstName {
            get { return user.FirstName; }
            set { user.FirstName = value; }
        }

        [XmlElement("lastName")]
        public string LastName
        {
            get { return user.LastName; }
            set { user.LastName = value; }
        }

        [XmlElement("email")]
        public string Email
        {
            get { return user.Email; }
            set { user.Email = value; }
        }

        [XmlArray("docList")]
        [XmlArrayItem("doc")]
        public List<Document> DocumentList {
            get { return user.DocumentList; }
            set { user.DocumentList = value; } 
        }

    }
    public class User
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public List<Document> DocumentList { get; set; }
    }
}

Upvotes: 2

Can DOGRU
Can DOGRU

Reputation: 69

public static class MyMethods
{       
    public static void XMLWrite<T>(this T obj, string PathXML) where T : class, new()
    {

            if (Directory.Exists(Path.GetDirectoryName(PathXML)))
            {
                using (FileStream stream = new FileStream(PathXML, FileMode.Create))
                using (XmlTextWriter writer = new XmlTextWriter(stream, Encoding.Unicode))
                {
                    XmlSerializer xmlSerializer = new XmlSerializer(obj.GetType());
                    xmlSerializer.Serialize(writer, obj);
                }
            }
    }

    public static T XMLRead<T>(this string PathXML) where T : class, new()
    {
            if (File.Exists(PathXML))
            {
                XmlSerializer xmlOkuyucu = new XmlSerializer(typeof(T));
                using (Stream okuyucu = new FileStream((PathXML), FileMode.Open))
                {
                    return (T)xmlOkuyucu.Deserialize(okuyucu);
                }
            }
            return default(T);
    }
 }

With generic method you can write or read your object which includes list.

Examples :

        object1 = MyMethods.XMLRead<YourClass>(string.Format("{0}\\{1}", locationXML, "file1.xml"));
        object2 = MyMethods.XMLRead<List<YourClass2>>(string.Format("{0}\\{1}", locationXML, "file2.xml"));

        object3.XMLWrite(Path.GetDirectoryName(Application.ExecutablePath) + "\\file3.xml");

Upvotes: 0

MacroMarc
MacroMarc

Reputation: 3324

Here are some functions that can be used. Build it up from the innermost with a ClassinstanceFromXML, and it will be completed :

    public static XElement GetTransaction(XDocument xDoc)
    {
        return xDoc.Descendants("transaction").FirstOrDefault();
    }

    public static Transaction TransactionFromXML(XElement transactionXElement)
    {
        string tranId = transactionXElement.Element("tranId").Value;
        string tranName = transactionXElement.Element("tranName").Value;
        //transform all <tranResult> Xelements into Class objects
        //by passing the xml content of those tags into the TransResultFromXML selector function
        List<TranResult> transResults = transactionXElement.Element("tranResultList")?
            .Elements("tranResult")?.Select(TransResultFromXML).ToList();

        Transaction t = new Transaction()
        {
            TranId = tranId,
            TranName = tranName,
            TranResultList = transResults
        };
        return t;
    }

    public static TranResult TransResultFromXML(XElement transResultElement)
    {
        string state = transResultElement.Element("state").Value;
        string dateCreated = transResultElement.Element("created").Value;
        List<Document> docList;
        //transform all <doc> Xelements in <docList> into Class objects
        //by passing the xml content of those tags into the DocumentFromXML selector function
        docList = transResultElement.Element("docList")?
            .Elements("doc")?.Select(DocumentFromXML).ToList();
        List<Role> roleList;

        //transform all <role> Xelements in <roleList> into Class objects
        //by passing the xml content of those tags into the RoleFromXML selector function
        roleList = transResultElement.Element("roleList")?
            .Elements("role")?.Select(RoleFromXML).ToList();

        return new TranResult()
        {
            State = state,
            DateCreated = dateCreated,
            DocumentList = docList,
            RoleList = roleList
        };
    }

    public static Role RoleFromXML(XElement roleElement)
    {
        string id = roleElement.Attribute("id").Value;
        string roleName = roleElement.Attribute("role").Value;
        //A similar apttern has been used above - do same here
        List<Document> userDocList = roleElement.Element("docList")?
            .Elements("doc")?.Select(DocumentFromXML).ToList();
        User myUser = new User()
        {
            FirstName = roleElement.Element("firstName")?.Value,
            LastName = roleElement.Element("lastName")?.Value,
            Email = roleElement.Element("email")?.Value,
            DocumentList = userDocList
        };
        return new Role()
        {
            Id = id,
            RoleName = roleName,
            user = myUser
        };
    }

    public static Document DocumentFromXML(XElement docElement)
    {
        return new Document()
        {
            Id = docElement.Attribute("id").Value,
            Name = docElement.Attribute("name").Value,
            status = docElement.Attribute("status")?.Value
        };
    }

    static void Main(string[] args)
    {
        XDocument x = XDocument.Load(@"Path\To\transactions.xml");
        Transaction myTransaction = TransactionFromXML(GetTransaction(x));

        Console.WriteLine(myTransaction.TranResultList.ElementAt(1).RoleList.ElementAt(0).user.Email);

        Console.ReadLine();
    }

Upvotes: 0

Related Questions