XML Deserialization not working past the first element - c#

I am attempting to use XML deserialization to convert a XML response from a host into C# objects. The root element is converted into its object and the 2nd element appears to be converted, but it's an empty element anyway. Beyond that, non of the other elements are converted. What am I missing?
I have tried many different variations of objects for each element and arrays of elements (even though an array is not needed. I even found that you can paste the XML into the editor in Visual Studio and it will create the serialized objects (edit, paste special, Paste XML As Classes), but I have not been able to get anything to work!
Here's how the XML looks:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE paymentService PUBLIC "-//MySite/DTD MySite PaymentService v1//EN" "http://dtd.mysite.com/paymentService_v1.dtd">
<paymentService version="1.4" merchantCode="ExampleCode1">
<reply>
<orderStatus orderCode="ExampleOrder1">
<reference id="YourReference">https://payments-test.mysite.com/app/hpp/integration/wpg/corporate?OrderKey=NGPPTESTMERCH1%5Ejsxml3835829684&Ticket=00146321872957902pqKhCTUf0vajKCw-X5HqZA</reference>
</orderStatus>
</reply>
</paymentService>
-OR-
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE paymentService PUBLIC "-//MySite//DTD MySite PaymentService v1//EN" "http://dtd.MySite.com/paymentService_v1.dtd">
<paymentService version="1.4" merchantCode="ExampleCode1">
<reply>
<orderStatus orderCode="ExampleOrder1">
<error code="2">
<![CDATA[Invalid address: Postal code is missing or empty.]]>
</error>
</orderStatus>
</reply>
</paymentService>
Here are the objects:
[Serializable]
[XmlRoot(ElementName = "paymentService")]
public partial class PaymentResponse
{
[XmlAttribute()]
public string version { get; set; }
[XmlAttribute()]
public string merchantCode { get; set; }
[XmlElement("reply")]
public Reply reply { get; set; }
}
[Serializable]
public partial class Reply
{
[XmlElement("orderStatus")]
public OrderStatus orderStatus {get; set; }
}
[Serializable]
public partial class OrderStatus
{
[XmlAttribute()]
public string orderCode {get; set; }
[XmlElement(ElementName = "reference",IsNullable =true)]
public Reference reference {get; set; }
[XmlElement(ElementName = "error",IsNullable =true)]
public Error error {get; set; }
}
Here's the call to do the deserialization:
XmlSerializer serializer = new XmlSerializer(typeof(PaymentResponse));
PaymentResponse response = (PaymentResponse)serializer.Deserialize(reader);
The only thing that is returned in the merchantcode and version from PaymentResponse.

You might have to tell the serializer about what to do about with the your non-primative types.
Try changing this
[XmlElement(ElementName = "reference",IsNullable =true)]
public Reference reference {get; set; }
[XmlElement(ElementName = "error",IsNullable =true)]
public Error error {get; set; }
to this
[XmlElement(ElementName = "reference", Type = typeof(Reference), IsNullable = true)]
public Reference reference {get; set; }
[XmlElement(ElementName = "error", Type = typeof(Error), IsNullable = true)]
public Error error {get; set; }
Good Luck!

Related

Cant deserialize XML with type attribute

I cant get the following XML deserialized because of the "type" attribute.
What I've tried so far:
The XML I want to deserialize:
<foundEntities>
<staticGroup xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns1:StaticGroupType">
<number>10000</number>
<name>Gruppe A</name>
</staticGroup>
</foundEntities>
The Class:
[XmlRootAttribute(Namespace = "", IsNullable = false, ElementName = "foundEntities")]
public class FoundEntities
{
[XmlElement(ElementName = "staticGroup", Namespace = "")]
public StaticGroup staticGroup { get; set; }
}
[XmlRoot(ElementName = "staticGroup")]
[XmlInclude(typeof(StaticGroupType))]
public class StaticGroup
{
[XmlElement(ElementName = "number")]
public string number { get; set; }
[XmlElement(ElementName = "name")]
public string name { get; set; }
}
public class StaticGroupType : StaticGroup
{
}
Probably I am blind but what do I miss here? Any hint is highly appreciated.
I have generated the classes using https://xmltocsharp.azurewebsites.net/ and it gave me the following output:
[XmlRoot(ElementName = "staticGroup")]
public class FoundStaticGroup
{
[XmlElement(ElementName = "number")]
public string Number { get; set; }
[XmlElement(ElementName = "name")]
public string Name { get; set; }
[XmlAttribute(AttributeName = "xsi", Namespace = "http://www.w3.org/2000/xmlns/")]
public string Xsi { get; set; }
[XmlAttribute(AttributeName = "type", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public string Type { get; set; }
}
[XmlRoot(ElementName = "foundEntities")]
public class FoundGroupEntities
{
[XmlElement(ElementName = "staticGroup")]
public StaticGroup StaticGroup { get; set; }
}
But what I get now is an error as already had a few times before:
"The specified type was not recognized: name='StaticGroupType', namespace='http://com.f24.soap.fwi.schema', at ."
I have posted a working solution here:
DotNetFiddle Link
I'm not sure if there are external schemas but I had to remove xsi:type="ns1:StaticGroupType" to get it to work. Your XML won't parse in most readers unless there is a definition for that namespace in the XML or a schema. Not knowing what your backend service is, I can't recommend how to resolve this.
The biggest issue is you had both your classes marked with the XmlRootAtribute code attribute. Only one class can represent the root in your example. I also removed unnecessary attributes like Namespace that don't have any impact since you are using the default namespace.
Update
The issue here is with the incoming XML it is not complete, it needs a schema reference to be able to resolve the StaticGroup type. You will have to contact the people on the backend to fix this, or strip that part of the XML manually. Correct XML would be as shown below:
<foundEntities
xsi:schemaLocation="http://com.f24.soap.fwi.schema (url to back-end schema here)">
<staticGroup xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns1:StaticGroupType">
<number>10000</number>
<name>Gruppe A</name>
</staticGroup>
</foundEntities>

Tricks for deserialize this xml element

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

List Property Not Being Populated on Deserialisation from XML

I am trying to deserialise an xml file to c# classes. I used an online tool to generate the classes for me as the structure of the XML file is quite complex. This worked well except for the population of repeated items into a List property in the main class.
I am using DotNet 4.5, C# in WPF.
A simplified version of the xml file is as follows:
<orderMessage xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:gs1:ecom:order:xsd:3">
<order xmlns="">
<creationDateTime>2017-07-10T00:00:00</creationDateTime>
<documentStatusCode>ORIGINAL</documentStatusCode>
<documentActionCode>ADD</documentActionCode>
</order>
<order xmlns="">
<creationDateTime>2017-07-10T00:00:00</creationDateTime>
<documentStatusCode>ORIGINAL</documentStatusCode>
<documentActionCode>ADD</documentActionCode>
</order>
</orderMessage>
The classes that I am using are as below:
[XmlRoot(ElementName = "order")]
public class Order
{
[XmlElement(ElementName = "creationDateTime")]
public string CreationDateTime { get; set; }
[XmlElement(ElementName = "documentStatusCode")]
public string DocumentStatusCode { get; set; }
[XmlElement(ElementName = "documentActionCode")]
public string DocumentActionCode { get; set; }
[XmlAttribute(AttributeName = "xmlns")]
public string Xmlns { get; set; }
}
[XmlRoot(ElementName = "orderMessage", Namespace = "urn:gs1:ecom:order:xsd:3")]
public class OrderMessage
{
[XmlElement(ElementName = "order")]
public List<Order> Order { get; set; }
[XmlAttribute(AttributeName = "xsd", Namespace = "http://www.w3.org/2000/xmlns/")]
public string Xsd { get; set; }
[XmlAttribute(AttributeName = "xsi", Namespace = "http://www.w3.org/2000/xmlns/")]
public string Xsi { get; set; }
[XmlAttribute(AttributeName = "xmlns")]
public string Xmlns { get; set; }
}
The deserialization code is as follows:
XmlSerializer serializer = new XmlSerializer(typeof(OrderMessage));
StreamReader reader = new StreamReader(filename);
OrderMessage newOrderMessage;
try
{
newOrderMessage = (OrderMessage)serializer.Deserialize(reader);
}
catch (Exception e)
{
throw e;
}
reader.Close();
When I run the code it runs without error but I end up with an empty list. There were other structures in the xml (that were not repeated structures - therefore no list property) that populated without problem that I have omitted.
I have looked at a number of questions similar to mine but they seem to suggest the same method I am using.
I am unable to change the XML as it is from a third party.
I would greatly appreciate it if anyone could point me in the right direction.
BTW - I know that catching an error and then throwing it is of no use whatsoever but I just did that to add a breakpoint so I could look at the inner exceptions if there were any. I will make the error handling more meaningful once I have the process working.
The issue is your Order property - the namespace will be inherited from OrderMessage, so it is urn:gs1:ecom:order:xsd:3 when it should be empty. You must specify this explicitly.
You can also remove a bunch of the namespace related attributes from your model. This is all you need:
[XmlRoot("orderMessage", Namespace = "urn:gs1:ecom:order:xsd:3")]
public class OrderMessage
{
[XmlElement("order", Namespace = "")]
public List<Order> Orders { get; set; }
}
public class Order
{
[XmlElement("creationDateTime")]
public string CreationDateTime { get; set; }
[XmlElement("documentStatusCode")]
public string DocumentStatusCode { get; set; }
[XmlElement("documentActionCode")]
public string DocumentActionCode { get; set; }
}
As an aside, throw e; in your deserialisation code is probably not what you want to do (see this question). Given you're not actually handling the exception, you can remove the try / catch entirely in this case.
You should also enclose your StreamReader in a using block to ensure it is disposed after use.

Empty List when deserializing XML to Object

I am having problems deserializing XML to an object with a list. The deserialize runs without error but the Resources list is empty, and I know there is one resource returned in the XML. It seems like it is just not being deserialised properly.
I am using the following code to deserialize
var ser = new XmlSerializer(typeof(SearchResult));
var test = result.Content.ReadAsStringAsync();
var t = (SearchResult)ser.Deserialize(result.Content.ReadAsStreamAsync().Result);
The variable "test" on line 2 of the code above is equal to
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<ns3:searchResult total="1" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:ns3="ers.ise.cisco.com">
<resources>
<resource id="76a4b0f2-64e2-11e6-9f15-00505688a404" name="5555884552">
<link rel="self" href="https://servername123:9060/ers/config/guestuser/76a4b0f2-64e2-11e6-9f15-00505688a404" type="application/xml"/>
</resource>
</resources>
</ns3:searchResult>
So I would expect to get one ExistingWifiAccountDto in the Resources list. But I don't. It is empty. What am I doing wrong?
The classes for object mapping are below
[XmlRoot(ElementName = "searchResult", Namespace = "ers.ise.cisco.com")]
public class SearchResult
{
public SearchResult()
{
Resources = new List<ExistingWifiAccountDto>();
}
[XmlArray("resources")]
[XmlArrayItem("resource", typeof(ExistingWifiAccountDto))]
public List<ExistingWifiAccountDto> Resources { get; set; }
}
public class ExistingWifiAccountDto
{
public ExistingWifiAccountDto()
{
}
[XmlAttribute("id")]
public string Id { get; set; }
[XmlAttribute("name")]
public string Name { get; set; }
[XmlElement("link")]
public LinkDto Link { get; set; }
}
public class LinkDto
{
public LinkDto()
{
}
[XmlAttribute("rel")]
public string Rel { get; set; }
[XmlAttribute("href")]
public string Href { get; set; }
[XmlAttribute("type")]
public string Type { get; set; }
}
You'll have to set the namespace to an empty string for the "resources" array since it is not inherited in you situation. This should then flow down to child elements.
Try changing
[XmlArray("resources")]
to
[XmlArray("resources", Namespace = "")]
This will cause it to deserialize correctly, alternatively, you could also set each node from "resources" down with the Form attribute:
Form = XmlSchemaForm.Unqualified
Cheers

Deserialize xml element that is empty but has attributes

I have some XML with an element like this:
<hour base_forecast="12" datim="29/0" />
And am receiving the error:
Unexpected node type Element. ReadElementString method can only be
called on elements with simple or empty content.
I am guessing this is because the element has no value. I don't control this XML so I can't change it. How would I deserialize this?
** EDIT **
One of the attributes' values is ">6" .... could this be the culprit? If so, how do I handle that?
** Update **
Found some data that wasn't returning a > in a value of the attribute. Same error is occurring.
** Edit #3 *
Created an XSD for the XML I am receiving, then generated classes for them with the xsd tool. Adding to the bottom of this post.
Here is the Deserialization code:
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("xxx");
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
WeatherData Result = new WeatherData();
using (Stream st = resp.GetResponseStream())
{
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = "model_data";
xRoot.IsNullable = true;
Result = new XmlSerializer(typeof(WeatherData), xRoot).Deserialize(st) as WeatherData; ** Error here
Xml returned:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE observation SYSTEM "http://private.com/hithere.dtd">
<model_data>
<site a="28/12" b="KXXX">
<hour x="-9999" y="-9999" z="-9999"/>
</site>
</model_data>
Data object
[Serializable, XmlRoot("model_data")]
public class WeatherData
{
[XmlElement("site")]
public string City { get; set; }
[XmlAttribute]
public string a { get; set; }
[XmlAttribute]
public string b { get; set; }
[XmlElement(ElementName="hour", IsNullable=true)]
public string Hour { get; set; }
[XmlAttribute]
public string x { get; set; }
[XmlAttribute]
public string y { get; set; }
[XmlAttribute]
public string z { get; set; }
}
XSD Tool generated classes
**Removed generated classes, but they are similar to what Hugo posted **
Looking at this part:
[XmlElement("site")]
public string City { get; set; }
<Site> contains <Hour>, so it is not an element with simple or empty content, I guess?
Edit: actually the whole thing seems suspect.
The data object seems to disregard all hierarchy information in the xml.
How about something like this?
[Serializable, XmlRoot("model_data")]
public class WeatherData
{
[XmlElement("site")]
public City[] City { get; set; }
}
public class City
{
[XmlAttribute]
public string a { get; set; }
[XmlAttribute]
public string b { get; set; }
[XmlElement(ElementName="hour", IsNullable=true)]
public Hour Hour { get; set; }
}
public class Hour
{
[XmlAttribute]
public string x { get; set; }
[XmlAttribute]
public string y { get; set; }
[XmlAttribute]
public string z { get; set; }
}
When I generated the classes with the XSD tool, I getting the same error, but the error was showing being thrown from a line I had commented out.
So I exited VS, and ran disk cleanup. Ran my code again. Received a message "For security reasons DTD is prohibited in this XML document. etc." So, I allowed set the reader to DtdProcessing.Parse, and ran the code once more.
It was successful.

Categories

Resources