Tricks for deserialize this xml element - c#

A legacy application transmits data as xml elements.
<frame>
<reply>
<id>value_id</id>
<resultCode>value</resultCode>
<readSampleIDs>
<returnValue>
<Sample>
<SampleID> value_SampleID </SampleID>
<SamplePC> value_SamplePC </SamplePC>
<antennaName> value_antennaName </antennaName>
<channel> value_channel </channel>
<power> value_power </power>
<polarization> value_polarization </polarization>
<inventoried> value_inventoried </inventoried>
</Sample>
…
<Sample>
…
</Sample>
</returnValue>
</readSampleIDs>
</reply>
Currently the information is extracted parsing the string word by word.
I think that xml element can be deserialized into an object directly with XmlSerializer but I have some doubts on how to do it.
The element frame contains only one reply. Do I really need two different classes ?
Inside returnValue there can be zero or more Sample. In my class what is the correct type, List<Sample> or Sample[] ? Is there a real difference between the two options during deserialization ?
Most fields inside Sample are optional. How do I model this ?
When an object is serialized with XmlSerializer information about xml version and additional attributes on root element are automatically added such as:
<?xml version="1.0" encoding="utf-8"?>
<PurchaseOrder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.cpandl.com">
They are not present in my example code so I'm afraid deserialization can complain about it and possibly fail.
Thanks
Filippo

If your xml contains a namespace (like in your example) then you can use these classes:
[XmlRoot(ElementName = "Sample")]
public class Sample
{
[XmlElement(ElementName = "SampleID")]
public string SampleID { get; set; }
[XmlElement(ElementName = "SamplePC")]
public string SamplePC { get; set; }
[XmlElement(ElementName = "antennaName")]
public string AntennaName { get; set; }
[XmlElement(ElementName = "channel")]
public string Channel { get; set; }
[XmlElement(ElementName = "power")]
public string Power { get; set; }
[XmlElement(ElementName = "polarization")]
public string Polarization { get; set; }
[XmlElement(ElementName = "inventoried")]
public string Inventoried { get; set; }
}
[XmlRoot(ElementName = "readSampleIDs")]
public class ReadSampleIDs
{
[XmlArray(ElementName = "returnValue")]
[XmlArrayItem(ElementName = "Sample")]
public List<Sample> Sample { get; set; }
}
[XmlRoot(ElementName = "reply", Namespace = "http://www.cpandl.com")]
public class Reply
{
[XmlElement(ElementName = "id")]
public string Id { get; set; }
[XmlElement(ElementName = "resultCode")]
public string ResultCode { get; set; }
[XmlElement(ElementName = "readSampleIDs")]
public ReadSampleIDs ReadSampleIDs { get; set; }
}
If you are only interested in node reply and this is the only one. You can deserialize only this node:
XNamespace loNameSpace = "http://www.cpandl.com";
XDocument loDoc = XDocument.Parse(Properties.Settings.Default.TransmitsData);
var loReplyElement = loDoc.Element(loNameSpace.GetName("PurchaseOrder"))
.Element(loNameSpace.GetName("frame"))
.Element(loNameSpace.GetName("reply"));
using (var loReader = loReplyElement.CreateReader())
{
var loSerializer = new XmlSerializer(typeof(Reply));
var loReply = (Reply)loSerializer.Deserialize(loReader);
Console.WriteLine(loReply.Id);
}

Related

Deserialize only specific Nodes from Xml to Object

I am trying to take specific Nodes from an Xml and write it into a class. I have this.
public class TradeMark
{
[XmlElement]
public string MarkVerbalElementText { get; set; }
[XmlElement]
public int MarkCurrentStatusCode { get; set; }
[XmlElement]
public string ExpiryDate { get; set; } = "";
}
static void Main(string[] args)
{
XmlSerializer serializer = new XmlSerializer(typeof(TradeMark));
using (TextReader reader = new StreamReader(pathToImportFile))
{
tradeMark = (TradeMark)serializer.Deserialize(reader);
}
}
In my Xml Data, there are more Node than just these 3. Now when i run the Code it says ...... was not expected. I guess bc. It tries to deserialize everything than only these 3 Infomartionen in Class TradeMark.
Can anyone help?
XML
<?xml version="1.0" encoding="UTF-8"?>
<Transaction xmlns="http://euipo.europa.eu/trademark/data" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://euipo.europa.eu/trademark/data http://euipo.europa.eu/schemas/trademark/EM-TM-TradeMark-V3-2.xsd">
<TransactionHeader>
<SenderDetails>
<RequestProducerDateTime>2018-08-18T15:33:35</RequestProducerDateTime>
</SenderDetails>
</TransactionHeader>
<TradeMarkTransactionBody>
<TransactionContentDetails>
<TransactionIdentifier>017690538</TransactionIdentifier>
<TransactionCode>EM-Trade Mark</TransactionCode>
<TransactionData>
<TradeMarkDetails>
<TradeMark operationCode="Insert">
<RegistrationOfficeCode>EM</RegistrationOfficeCode>
<ApplicationNumber>017690538</ApplicationNumber>
<ApplicationDate>2018-01-16</ApplicationDate>
<RegistrationDate>2018-06-14</RegistrationDate>
<ApplicationLanguageCode>en</ApplicationLanguageCode>
<SecondLanguageCode>es</SecondLanguageCode>
<ExpiryDate>2028-01-16</ExpiryDate>
<MarkCurrentStatusCode milestone="23" status="1">Registered</MarkCurrentStatusCode>
<MarkCurrentStatusDate>2018-06-15</MarkCurrentStatusDate>
<KindMark>Individual</KindMark>
<MarkFeature>Figurative</MarkFeature>
<TradeDistinctivenessIndicator>false</TradeDistinctivenessIndicator>
<WordMarkSpecification>
<MarkVerbalElementText>Tiens</MarkVerbalElementText>
</WordMarkSpecification>
Most likely this happens because your XML has a default namespace and Transaction is within this namespace.
You need to mark your class with XmlRootAttribute like so:
[XmlRootAttribute("TradeMark", Namespace="http://euipo.europa.eu/trademark/data",
IsNullable = false)]
public class TradeMark
XmlIgnore is what you're looking for.
MSDN Docs
See clarification in this answer, as the docs only state XmlIgnore will be ignored on serialize, but will also be ignored when deserializing.
From your example:
public class TradeMark
{
[XmlElement]
public string MarkVerbalElementText { get; set; }
[XmlElement]
public int MarkCurrentStatusCode { get; set; }
[XmlElement]
public string ExpiryDate { get; set; } = "";
[XmlIgnore]
public string IgnoreMe { get; set; } // This will be ignored
}

Null value on xml deserialization using [XmlAttribute]

I have the following XML;
<?xml version="1.0" encoding="UTF-8" ?>
<feedback>
<report_metadata>
<org_name>example.com</org_name>
</report_metadata>
</feedback>
and the following Feedback.cs class;
[XmlRoot("feedback", Namespace = "", IsNullable = false)]
public class Feedback
{
[XmlElement("report_metadata")]
public MetaData MetaData { get; set; }
}
[XmlType("report_metadata")]
public class MetaData
{
[XmlAttribute("org_name")]
public string Organisation { get; set; }
}
When I attempt to deserialize, the value for Organisation is null.
var xml = System.IO.File.ReadAllText("example.xml");
var serializer = new XmlSerializer(typeof(Feedback));
using (var reader = new StringReader(input))
{
var feedback = (Feedback)serializer.Deserialize(reader);
}
Yet, when I change Feedback.cs to the following, it works (obviously the property name has changed).
[XmlType("report_metadata")]
public class MetaData
{
//[XmlAttribute("org_name")]
public string org_name { get; set; }
}
I want the property to be Organisation, not org_name.
In the example XML file org_name is an XML element, not an XML attribute. Changing
[XmlAttribute("org_name")] to [XmlElement("org_name")] at the Organisation property will deserialize it as an element:
[XmlElement("org_name")]
public string Organisation { get; set; }
probably just typo
[XmlAttribute("org_name")]
public string Organisation { get; set; }
was supposed to be
[XmlElement("org_name")]
public string Organisation { get; set; }
Try to modify your Xml classes like
[XmlRoot(ElementName = "report_metadata")]
public class MetaData
{
[XmlElement(ElementName = "org_name")]
public string Organisation { get; set; }
}
[XmlRoot(ElementName = "feedback")]
public class Feedback
{
[XmlElement(ElementName = "report_metadata")]
public MetaData MetaData { get; set; }
}
Then you will get your desired output like
class Program
{
static void Main(string[] args)
{
Feedback feedback = new Feedback();
var xml = System.IO.File.ReadAllText(#"C:\Users\Nullplex6\source\repos\ConsoleApp4\ConsoleApp4\Files\XMLFile1.xml");
var serializer = new XmlSerializer(typeof(Feedback));
using (var reader = new StringReader(xml))
{
feedback = (Feedback)serializer.Deserialize(reader);
}
Console.WriteLine($"Organization: {feedback.MetaData.Organisation}");
Console.ReadLine();
}
}
Output:

XML Deserialization error: xxxxx was not expected

I know there are several posts out there with this topic, but I can't seem to figure out what is the problem here. I have serialized and deserialized xml several times, and never had this error.
The exception message is:
There is an error in XML document (1, 2).
With InnerException:
<InvoiceChangeRequest xmlns=''> was not expected.
XML file I want to deserialize:
<ns1:InvoiceChangeRequest xmlns:ns1="http://kmd.dk/fie/external_invoiceDistribution">
<CONTROL_FIELDS>
<STRUCTURID>0000000001</STRUCTURID>
<OPERA>GET</OPERA>
<WIID>000050371220</WIID>
</CONTROL_FIELDS>
<HEADER_IN>
<MANDT>751</MANDT>
<BELNR>1234567890</BELNR>
</HEADER_IN>
<ITEMS>
<ITEM_FIELDS_IN>
<BUZEI>001</BUZEI>
<BUKRS>0020</BUKRS>
</ITEM_FIELDS_IN>
</ITEMS>
</ns1:InvoiceChangeRequest>
Class I'm trying to deserialize to:
[XmlRoot(Namespace = "http://kmd.dk/fie/external_invoiceDistribution", IsNullable = false)]
public class InvoiceChangeRequest
{
[XmlElement("CONTROL_FIELDS")] public ControlFields Styrefelter;
[XmlElement("HEADER_IN")] public HeaderIn HeaderfelterInd;
[XmlElement("ITEMS")] public Items Linjer;
}
public class HeaderIn
{
[XmlElement("MANDT")] public string Kommunenummer;
[XmlElement("BELNR")] public string RegnskabsbilagsNummer;
}
public class Items
{
[XmlElement("ITEM_FIELDS_IN")] public Itemfield[] ItemfelterInd;
}
public class Itemfield
{
[XmlElement("BUZEI")] public string Linjenummer;
[XmlElement("BUKRS")] public string Firmakode;
}
Deserialization code:
XmlSerializer serializer = new XmlSerializer(typeof(InvoiceChangeRequest));
var request = serializer.Deserialize(new StringReader(output)) as InvoiceChangeRequest;
In your XML file your root element is the namespace http://kmd.dk/fie/external_invoiceDistribution with prefix ns1.
The element <CONTROL_FIELDS> isn't because it isn't prefixed. Your serialization class doesn't take this into account though. That means that it expects that <CONTROL_FIELDS> and the other elements are ALSO in the ns1 namespace.
To get the serializer parse the elements correctly add the Namespace to the elements, setting it to an empty string:
[XmlRoot(Namespace = "http://kmd.dk/fie/external_invoiceDistribution", IsNullable = false)]
public class InvoiceChangeRequest
{
[XmlElement("CONTROL_FIELDS", Namespace = "")]
public ControlFields Styrefelter { get; set; }
[XmlElement("HEADER_IN", Namespace = "")]
public HeaderIn HeaderfelterInd { get; set; }
[XmlElement("ITEMS", Namespace = "")]
public Items Linjer { get; set; }
}
This will de-serialize the given XML as intended.
In case of de-serialization issues I often create the classes in memory and then serialize that so I can inspect the resulting XML. That often gives clues on what is missing or being added compared to the input document:
var ms = new MemoryStream();
serializer.Serialize(ms, new InvoiceChangeRequest {
Styrefelter = new ControlFields { Opera="test"}
});
var s = Encoding.UTF8.GetString(ms.ToArray());
And then inspect s for differences.
You can replace 'ns1:' with string.Empty.
Below classes should serialize.
public class Item
{
[XmlElement("BUZEI")]
public string Buzei { get; set; }
[XmlElement("BUKRS")]
public string Bukrs { get; set; }
}
public class Header
{
[XmlElement("MANDT")]
public string Mandt { get; set; }
[XmlElement("BELNR")]
public string Belnr { get; set; }
}
public class ControlFields
{
[XmlElement("STRUCTURID")]
public string StructuredId { get; set; }
[XmlElement("OPERA")]
public string Opera { get; set; }
[XmlElement("WIID")]
public string Wild { get; set; }
}
public class InvoiceChangeRequest
{
[XmlElement("CONTROL_FIELDS")]
public ControlFields ControlFields { get; set; }
[XmlElement("HEADER_IN")]
public Header Header { get; set; }
[XmlArray("ITEMS")]
[XmlArrayItem("ITEM_FIELDS_IN")]
public List<Item> Items { get; set; }
}

XML Deserialization with multiple namespaces

I'm trying to deserialize the following xml into an Object.
Xml got multiple namespaces.
I tried to deserialize the Xml into an object.
The object (data) has a reference to the LastChannel Object.
But when i ask for data.channel which should give me the LastChannel, i get a nullpointer.
Xml:
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://purl.org/rss/1.0/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:mp="http://www.tagesschau.de/rss/1.0/modules/metaplus/"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
<title>title</title>
<description>Default description</description>
<dc:date>2013-04-15 13:27:06</dc:date>
<sy:updateBase>2013-04-15 13:27:06</sy:updateBase>
<sy:updatePeriod>hourly</sy:updatePeriod>
<sy:updateFrequency>12</sy:updateFrequency>
</channel>
</rdf:RDF>
The objects look like this:
[XmlRoot("RDF", Namespace = "http://www.w3.org/1999/02/22-rdf-syntax-ns#")]
public class LastRss
{
[XmlElement("channel")]
public LastChannel channel { get; set; }
}
and
public class LastChannel
{
[XmlElement("title")]
public string title { get; set; }
[XmlElement("description")]
public string description { get; set; }
[XmlElement("date", Namespace = "http://purl.org/dc/elements/1.1/")]
public DateTime date { get; set; }
[XmlElement("updateBase", Namespace = "http://purl.org/rss/1.0/modules/syndication/")]
public DateTime updateBase { get; set; }
[XmlElement("updatePeriod", Namespace = "http://purl.org/rss/1.0/modules/syndication/")]
public string updatePeriod { get; set; }
[XmlElement("updateFrequency", Namespace = "http://purl.org/rss/1.0/modules/syndication/")]
public int updateFrequency { get; set; }
}
Anybody sees why the data.channel ist null?
Serializer:
LastRss data = new LastRss();
XmlSerializer serializer = new XmlSerializer(typeof(LastRss));
System.IO.TextReader reader = new System.IO.StringReader(xml);
try
{
object o = serializer.Deserialize(reader);
data = (LastRss)o;
}
Your channel is in the default xmlns, viz http://purl.org/rss/1.0/
[XmlElement("channel", Namespace = "http://purl.org/rss/1.0/")]
public LastChannel channel { get; set; }
You'll also need to correct the date formats e.g. 2013-04-15**T**13:27:06

how to deserialize xml to list in RestSharp?

My XML:
<result>
<document version="2.1.0">
<response type="currency">
<currency>
<code>AMD</code>
<price>85.1366</price>
</currency>
</response>
<response type="currency">
<currency>
<code>AUD</code>
<price>31.1207</price>
</currency>
</response>
</document>
</result>
My Class:
public class CurrencyData
{
public string Code { get; set; }
public string Price { get; set; }
}
My deserializer calling:
RestClient.ExecuteAsync<List<CurrencyData>>...
If i renamed class CurrencyData to Currency then all will been done right. But I want to keep this class name.
Ok, I think I got it,
You can try RestClient.ExecuteAsync<Result>()
[XmlRoot("result")]
public class Result
{
[XmlElement("document")]
public Document Document { get; set; }
}
public class Document
{
[XmlElement("response")]
public Response[] Responses { get; set; }
[XmlAttribute("version")]
public string Version { get; set; }
}
public class Response
{
[XmlElement("currency")]
public CurrencyData Currency { get; set; }
[XmlAttribute("type")]
public string Type { get; set; }
}
public class CurrencyData
{
[XmlElement("code")]
public string Code { get; set; }
[XmlElement("price")]
public decimal Price { get; set; }
}
I had to add a few XmlElement attribute to override the casing without having to name classes and properties in lowercase. but you can drop them if you can change the xml to match the casing
Answer of kay.one is perfect! It works with a any remarks:
public List<Response> Responses { get; set; }
works
public Response[] Responses { get; set; }
don`t works
And
[XmlElement("AnyValue")]
it is from System.Xml.Serialization namespace and don`t work.
Feel free to just delete them.
In this example annotation attributes and properties has same names and serializer understands.
But right annotation attributes are from RestSharp.Deserializers namespace
[DeserializeAs(Name="AnyXmlValue")]
public string AnyModelValue { get; set; }
How to manage deserialization of RestSharp
Then change the xml tag to CurrencyData. Here is the documentation about the xml deserializer: https://github.com/restsharp/RestSharp/wiki/Deserialization
I'm not sure why Kay.one's answer is accepted, it doesn't answer the question.
Per my comment, the default RestSharp deserializer doesn't check for the DeserializeAs attribute when deserializing a list. I'm not sure if that's intentional or a mistake as the author doesn't seem to be very available.
Anyways it's a simple fix.
private object HandleListDerivative(object x, XElement root, string propName, Type type)
{
Type t;
if (type.IsGenericType)
{
t = type.GetGenericArguments()[0];
}
else
{
t = type.BaseType.GetGenericArguments()[0];
}
var list = (IList)Activator.CreateInstance(type);
var elements = root.Descendants(t.Name.AsNamespaced(Namespace));
var name = t.Name;
//add the following
var attribute = t.GetAttribute<DeserializeAsAttribute>();
if (attribute != null)
name = attribute.Name;

Categories

Resources