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
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));
}
}
}