Home > OS >  Deserialize XML with different type rows C#
Deserialize XML with different type rows C#

Time:07-04

Have a problem with deserialization XML. Can not understand how make property data different based on xml attribute id xds generate one common property data that combine properties from data UserInfo data UserTransactions

Tried solution from Watch

CodePudding user response:

You cannot use an inherited class with your xml. Inherited classes requires a type attribute in the xml. Instead you can use a custom IXmlSerialize like code below

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

namespace ConsoleApp2
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            XmlReaderSettings settings = new XmlReaderSettings();
            settings.IgnoreWhitespace = true;
            XmlReader reader = XmlReader.Create(FILENAME);
            XmlSerializer serializer = new XmlSerializer(typeof(test.document));
            test.document doc = (test.document)serializer.Deserialize(reader);
 

        }

    }
 
}
namespace test
{
    public class document
    {        
        [XmlElement("data")]
        public documentData[] data { get; set; }
    }
    public class documentData : IXmlSerializable
    {
        [XmlAttribute("id")]
        public string id { get; set; }

        public List<UserInfoRow> userInfoRows { get; set; }
        public List<UserTransactionRow> userTransactionRows { get; set; }

        public void WriteXml(XmlWriter writer)
        {
 
        }

        public void ReadXml(XmlReader reader)
        {
            id = reader.GetAttribute("id");       
            XmlSerializer serializer = null;
            XmlReader xmlChildReader = null; 
            switch (id)
            {
                case "UserInfo":
                    serializer = new XmlSerializer(typeof(UserInfoRow)) ;
                    xmlChildReader = reader.ReadSubtree();
                    while (xmlChildReader.ReadToFollowing("row"))
                    {     
                        UserInfoRow userRow = (UserInfoRow)serializer.Deserialize(xmlChildReader);
                        if (userInfoRows == null) userInfoRows = new List<UserInfoRow>();
                        userInfoRows.Add(userRow);
                    }
                    break;
                case "UserTransactions":
                    serializer = new XmlSerializer(typeof(UserTransactionRow));
                    xmlChildReader = reader.ReadSubtree();
                    while (xmlChildReader.ReadToFollowing("row"))
                    {
                        UserTransactionRow userRow = (UserTransactionRow)serializer.Deserialize(xmlChildReader);
                        if (userTransactionRows == null) userTransactionRows = new List<UserTransactionRow>();
                        userTransactionRows.Add(userRow);
                    }
                    break;
            }
            reader.ReadEndElement();

        }

        public XmlSchema GetSchema()
        {
            return (null);
        }

    }
    [XmlRoot("row")]
    public class UserInfoRow
    {
        [XmlAttribute("id")]
        public string id { get; set; }
        [XmlAttribute("Name")]
        public string Name { get; set; }
        [XmlAttribute("phone")]
        public string phone { get; set; }
    }
    [XmlRoot("row")]
    public class UserTransactionRow
    {
        [XmlAttribute("UserId")]
        public string UserId { get; set; }
        [XmlAttribute("amount")]
        public string amount { get; set; }
        [XmlAttribute("date")]
        public string date { get; set; }
    }
}

Here is another solution that is more compact. I eliminated the documentData class

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

namespace ConsoleApp2
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            XmlReaderSettings settings = new XmlReaderSettings();
            settings.IgnoreWhitespace = true;
            XmlReader reader = XmlReader.Create(FILENAME);
            XmlSerializer serializer = new XmlSerializer(typeof(test.document));
            test.document doc = (test.document)serializer.Deserialize(reader);
 

        }

    }
 
}
namespace test
{
    public class document : IXmlSerializable
    {        
 
        public string id { get; set; }

        public List<UserInfoRow> userInfoRows { get; set; }
        public List<UserTransactionRow> userTransactionRows { get; set; }

        public void WriteXml(XmlWriter writer)
        {
 
        }

        public void ReadXml(XmlReader reader)
        {
            XmlSerializer serializer = null;
            XmlReader xmlChildReader = null;

            while (reader.ReadToFollowing("data"))
            {

                id = reader.GetAttribute("id");
                switch (id)
                {
                    case "UserInfo":
                        serializer = new XmlSerializer(typeof(UserInfoRow));
                        xmlChildReader = reader.ReadSubtree();
                        while (xmlChildReader.ReadToFollowing("row"))
                        {
                            UserInfoRow userRow = (UserInfoRow)serializer.Deserialize(xmlChildReader);
                            if (userInfoRows == null) userInfoRows = new List<UserInfoRow>();
                            userInfoRows.Add(userRow);
                        }
                        break;
                    case "UserTransactions":
                        serializer = new XmlSerializer(typeof(UserTransactionRow));
                        xmlChildReader = reader.ReadSubtree();
                        while (xmlChildReader.ReadToFollowing("row"))
                        {
                            UserTransactionRow userRow = (UserTransactionRow)serializer.Deserialize(xmlChildReader);
                            if (userTransactionRows == null) userTransactionRows = new List<UserTransactionRow>();
                            userTransactionRows.Add(userRow);
                        }
                        break;
                }
            }

        }

        public XmlSchema GetSchema()
        {
            return (null);
        }

    }
    [XmlRoot("row")]
    public class UserInfoRow
    {
        [XmlAttribute("id")]
        public string id { get; set; }
        [XmlAttribute("Name")]
        public string Name { get; set; }
        [XmlAttribute("phone")]
        public string phone { get; set; }
    }
    [XmlRoot("row")]
    public class UserTransactionRow
    {
        [XmlAttribute("UserId")]
        public string UserId { get; set; }
        [XmlAttribute("amount")]
        public string amount { get; set; }
        [XmlAttribute("date")]
        public string date { get; set; }
    }
}

CodePudding user response:

The problem is that XmlInclude expects an xsi:type attribute, which you don't have.

You can include it manually, by first deserializing into an `XDocument, modifying that, then deserializing your objects from that

 XmlSerializer _serializer = new XmlSerializer(typeof(document));  // always cache

 public static void Main()
 {
     var xml = @"<?xml version=""1.0"" encoding=""UTF-8""?>
<document>
<data id=""UserInfo"">
 <rows>
   <row id=""123"" Name=""Alex"" phone="" 1234567890""></row>
   <row id=""321"" Name=""Sally"" phone="" 1234567890""></row>
 </rows>
</data>
<data id=""UserTransactions"">
 <rows>
   <row UserId=""123"" amount=""100.1"" date=""2022-07-03""></row>
   <row UserId=""123"" amount=""-100.1"" date=""2022-07-03""></row>
   <row UserId=""321"" amount=""1"" date=""2022-07-03""></row>
 </rows>
</data>
</document>
";
     var xdoc = XDocument.Parse(xml);
     AddTypeDefinition(xdoc);
     Console.WriteLine(xdoc.ToString());
     var obj = (document)_serializer.Deserialize(xdoc.CreateReader());
     Console.WriteLine(obj.data[0].rows[0].GetType().Name);
 }
 private static void AddTypeDefinition(XDocument document)
 {
     // XNamespace always converts from string implicitly
     XNamespace xsi = "http://www.w3.org/2001/XMLSchema-instance";
     document.Root.Add(new XAttribute(XNamespace.Xmlns   "xsi", xsi));
     foreach(var node in document.Root.Elements("data"))
     {
         string typeName;
         // decide based on "id" attribute
         if(node.Attribute("id").Value == "UserInfo")
             typeName = "UserInfoRow";
         else if(node.Attribute("id").Value == "UserInfo")
             typeName = "UserTransactionRow";
         else
             continue;

         foreach (var row in node.Element("rows").Elements("row"))
         {
             // xsi   "type" creates an XName with the namespace name
             // we have already added that declaration so becomes "xsi:type"
             row.Add(new XAttribute(xsi   "type", typeName));
         }
     }
 }

dotnetfiddle

  • Related