Deserialize xml element that is empty but has attributes - c#

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.

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
}

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.

Deserializing an XML array from a web api

First off, I followed the answer given here, but I still can not get the following to work.
I am retrieving XML from a web API, and the results returned are as such:
<ArrayOf__ptd_student_charges
xmlns="http://schemas.datacontract.org/2004/07/something.something"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<__ptd_student_charges>
<accumulated_tuition>000.000</accumulated_tuition>
<course_id>AAA-000/L</course_id>
<invoice_date>01/01/2015</invoice_date>
<lecturer_name>John Doe</lecturer_name>
<net_tuition>000.000</net_tuition>
<section_no>1</section_no>
<semester>Summer</semester>
<student_id>123456</student_id>
<student_name>John Doe</student_name>
<year>2015</year>
</__ptd_student_charges>
<__ptd_student_charges>
<accumulated_tuition>000.000</accumulated_tuition>
<course_id>AAA-000/L</course_id>
<invoice_date>01/01/2015</invoice_date>
<lecturer_name>John Doe</lecturer_name>
<net_tuition>000.000</net_tuition>
<section_no>1</section_no>
<semester>Summer</semester>
<student_id>123456</student_id>
<student_name>John Doe</student_name>
<year>2015</year>
</__ptd_student_charges>
</ArrayOf__ptd_student_charges>
I'm trying to deserialize this into an array of students.
My student class is defined like this:
public class Student
{
[System.Xml.Serialization.XmlElement("accumulated_tuiton")]
public double AccumulatedTution { get; set; }
[System.Xml.Serialization.XmlElement("net_tuiton")]
public double NetTuiton { get; set; }
[System.Xml.Serialization.XmlElement("course_id")]
public string CourseID { get; set; }
[System.Xml.Serialization.XmlElement("invoice_date")]
public DateTime InvoiceDate { get; set; }
[System.Xml.Serialization.XmlElement("lecturer_name")]
public string LecturerName { get; set; }
[System.Xml.Serialization.XmlElement("semester")]
public string Semester { get; set; }
[System.Xml.Serialization.XmlElement("student_id")]
public string StudentId { get; set; }
[System.Xml.Serialization.XmlElement("student_name")]
public string StudentName { get; set; }
[System.Xml.Serialization.XmlElement("year")]
public string Year { get; set; }
[System.Xml.Serialization.XmlElement("section_no")]
public int Section { get; set; }
}
And my student collection is defined like this:
[System.Xml.Serialization.XmlRoot("ArrayOf__ptd_student_charges xmlns=\"http://schemas.datacontract.org/2004/07/something.something\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance")]
public class StudentCollection
{
[XmlArray("ArrayOf__ptd_student_charges")]
[XmlArrayItem("__ptd_student_charges", typeof(Student))]
public Student[] StudentArray { get; set; }
}
I'm deserializing the results using this code:
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
StudentCollection collection;
HttpWebRequest request = WebRequest.Create(stringUrl) as HttpWebRequest;
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
XmlTextReader reader = new XmlTextReader(response.GetResponseStream());
XmlSerializer serializer = new XmlSerializer(typeof(StudentCollection));
collection = (StudentCollection)serializer.Deserialize(reader);
reader.Close();
}
Once I run this, I get an InvalidOperationException with a message
ArrayOf__ptd_student_charges
xmlns='http://schemas.datacontract.org/2004/07/something.something'>
was not expected.
I know that the xmlns:... shouldn't be in the first tag, but unfortunately it is and I'm unsure on how to proceed.
Basically, you need to support the default XML namespace in your XML file - you can either do this by specifying it on the StudentCollection:
[System.Xml.Serialization.XmlRoot("ArrayOf__ptd_student_charges")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://schemas.datacontract.org/2004/07/something.something", IsNullable = false)]
public class StudentCollection
{
[XmlArray("ArrayOf__ptd_student_charges")]
[XmlArrayItem("__ptd_student_charges", typeof(Student))]
public Student[] StudentArray { get; set; }
}
and the actual Student class:
[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://schemas.datacontract.org/2004/07/something.something", IsNullable = false)]
public class Student
{
..........
}
or you can specify it programmatically when you deserialize:
XmlSerializer serializer = new XmlSerializer(typeof(StudentCollection),
"http://schemas.datacontract.org/2004/07/something.something");
That second parameter for the XmlSerializer is the default XML namespace to use when deserializing the XML content.
Extra tipp: if you ever have an XML file again, and you need to get the C# code classes that represent that XML - if you have Visual Studio 2012 or newer, just create a new code class, copy your XML file into the clipboard, and then use Edit > Paste Special > Paste XML as classes and you get all your C# including all XML attribute and XML namespaces and everything pasted into your Visual Studio right there

Alter XML root name [duplicate]

First off, I followed the answer given here, but I still can not get the following to work.
I am retrieving XML from a web API, and the results returned are as such:
<ArrayOf__ptd_student_charges
xmlns="http://schemas.datacontract.org/2004/07/something.something"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<__ptd_student_charges>
<accumulated_tuition>000.000</accumulated_tuition>
<course_id>AAA-000/L</course_id>
<invoice_date>01/01/2015</invoice_date>
<lecturer_name>John Doe</lecturer_name>
<net_tuition>000.000</net_tuition>
<section_no>1</section_no>
<semester>Summer</semester>
<student_id>123456</student_id>
<student_name>John Doe</student_name>
<year>2015</year>
</__ptd_student_charges>
<__ptd_student_charges>
<accumulated_tuition>000.000</accumulated_tuition>
<course_id>AAA-000/L</course_id>
<invoice_date>01/01/2015</invoice_date>
<lecturer_name>John Doe</lecturer_name>
<net_tuition>000.000</net_tuition>
<section_no>1</section_no>
<semester>Summer</semester>
<student_id>123456</student_id>
<student_name>John Doe</student_name>
<year>2015</year>
</__ptd_student_charges>
</ArrayOf__ptd_student_charges>
I'm trying to deserialize this into an array of students.
My student class is defined like this:
public class Student
{
[System.Xml.Serialization.XmlElement("accumulated_tuiton")]
public double AccumulatedTution { get; set; }
[System.Xml.Serialization.XmlElement("net_tuiton")]
public double NetTuiton { get; set; }
[System.Xml.Serialization.XmlElement("course_id")]
public string CourseID { get; set; }
[System.Xml.Serialization.XmlElement("invoice_date")]
public DateTime InvoiceDate { get; set; }
[System.Xml.Serialization.XmlElement("lecturer_name")]
public string LecturerName { get; set; }
[System.Xml.Serialization.XmlElement("semester")]
public string Semester { get; set; }
[System.Xml.Serialization.XmlElement("student_id")]
public string StudentId { get; set; }
[System.Xml.Serialization.XmlElement("student_name")]
public string StudentName { get; set; }
[System.Xml.Serialization.XmlElement("year")]
public string Year { get; set; }
[System.Xml.Serialization.XmlElement("section_no")]
public int Section { get; set; }
}
And my student collection is defined like this:
[System.Xml.Serialization.XmlRoot("ArrayOf__ptd_student_charges xmlns=\"http://schemas.datacontract.org/2004/07/something.something\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance")]
public class StudentCollection
{
[XmlArray("ArrayOf__ptd_student_charges")]
[XmlArrayItem("__ptd_student_charges", typeof(Student))]
public Student[] StudentArray { get; set; }
}
I'm deserializing the results using this code:
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
StudentCollection collection;
HttpWebRequest request = WebRequest.Create(stringUrl) as HttpWebRequest;
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
XmlTextReader reader = new XmlTextReader(response.GetResponseStream());
XmlSerializer serializer = new XmlSerializer(typeof(StudentCollection));
collection = (StudentCollection)serializer.Deserialize(reader);
reader.Close();
}
Once I run this, I get an InvalidOperationException with a message
ArrayOf__ptd_student_charges
xmlns='http://schemas.datacontract.org/2004/07/something.something'>
was not expected.
I know that the xmlns:... shouldn't be in the first tag, but unfortunately it is and I'm unsure on how to proceed.
Basically, you need to support the default XML namespace in your XML file - you can either do this by specifying it on the StudentCollection:
[System.Xml.Serialization.XmlRoot("ArrayOf__ptd_student_charges")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://schemas.datacontract.org/2004/07/something.something", IsNullable = false)]
public class StudentCollection
{
[XmlArray("ArrayOf__ptd_student_charges")]
[XmlArrayItem("__ptd_student_charges", typeof(Student))]
public Student[] StudentArray { get; set; }
}
and the actual Student class:
[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://schemas.datacontract.org/2004/07/something.something", IsNullable = false)]
public class Student
{
..........
}
or you can specify it programmatically when you deserialize:
XmlSerializer serializer = new XmlSerializer(typeof(StudentCollection),
"http://schemas.datacontract.org/2004/07/something.something");
That second parameter for the XmlSerializer is the default XML namespace to use when deserializing the XML content.
Extra tipp: if you ever have an XML file again, and you need to get the C# code classes that represent that XML - if you have Visual Studio 2012 or newer, just create a new code class, copy your XML file into the clipboard, and then use Edit > Paste Special > Paste XML as classes and you get all your C# including all XML attribute and XML namespaces and everything pasted into your Visual Studio right there

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