I have this xml response as a stream from USPS 3.0 zip code look up api
<ZipCodeLookupResponse>
<Address ID="1">
<Address1>Unit 2</Address1>
<Address2>8 WILDWOOD DR</Address2>
<City>OLD LYME</City>
<State>CT</State>
<Zip5>06371</Zip5>
</Address>
<Address ID="2">
<Address1>Unit 2</Address1>
<Address2>8 WILDWOOD DR</Address2>
<City>OLD LYME</City>
<State>CT</State>
<Zip5>06371</Zip5>
</Address>
</ZipCodeLookupResponse>
and I'm trying to deserialize the response in an array of addresses.
[XmlRoot(ElementName = "ZipCodeLookupResponse")]
public class UspsAddress
{
[XmlArray("Address")]
public UspsAddressResult[] Addresses {get;set;}
}
[XmlRoot(ElementName = "Address")]
public class UspsAddressResult
{
[XmlElement(ElementName = "Address2")]
public string Adress1 { get; set; }
[XmlElement(ElementName = "Address1")]
public string Adress2 { get; set; }
[XmlElement(ElementName = "City")]
public string City { get; set; }
[XmlElement(ElementName = "State")]
public string State { get; set; }
[XmlElement(ElementName = "Zip5")]
public string Zip { get; set; }
}
This code is always returning an empty array. How can I fix this code to get both address from the response?
...
var content = await res.Content.ReadAsStreamAsync();
var serializer = new XmlSerializer(typeof(UspsAddress));
var results = (UspsAddress)serializer.Deserialize(content);
Instead of using XmlArray or XmlArrayItem, you can use the `XmlElement``
attribute to set the name of the child elements. The deserializer will recognize that there are multiple elements that are supposed to be deserialized into a list of objects.
Your class then looks like this:
[XmlRoot(ElementName = "ZipCodeLookupResponse")]
public class UspsAddress
{
[XmlElement("Address")]
public UspsAddressResult[] Addresses {get;set;}
}
Related
I need to parse an XML and store it into a class but it's been giving me a lot more headaches than I'm happy to admit.
Assume the XML looks like this:
<PARENT>
<ITEM>
<DATA1>
<A>12345</A>
<B>12345</B>
<C>12345</C>
</DATA1>
<DATA2>
<D>12345</D>
<E>12345</E>
<F>12345</F>
</DATA2>
<DATA3>
<ITEM>
<G/>
<H>111</H>
<I>223</I>
</ITEM>
<ITEM>
<G/>
<H>2342</H>
<I>25323</I>
</ITEM>
<ITEM>
<G>185</G>
<H>63611</H>
<I/>
</ITEM>
</DATA3>
</ITEM>
<ITEM>
<DATA1>
<A>23456</A>
<B>23456</B>
<C>23456</C>
</DATA1>
</ITEM>
</PARENT>
The first problem I'm facing is the fact that the name "ITEM" doesn't only appear as a childnode of "PARENT" but is also reused again under of "DATA3" so if I just search like this
XDocument doc = XDocument.Parse(tmp);
IEnumerable<XElement> tmp_list = doc.Descendants(XName.Get("ITEM"));
it will give me gives me five results instead of just two.
I've also already tried the approach of searching the path //PARENT/ITEM like this:
string file = #"C:\temp\test.xml";
var document = XDocument.Load(file);
var namespaceManager = new XmlNamespaceManager(new NameTable());
namespaceManager.AddNamespace("empty", "random:namespace:name");
IEnumerable<XElement> ele_list = document.XPathSelectElements("//PARENT//ITEM", namespaceManager);
but it doesn't work either since it also fetches the all the grandchild nodes...
Question 1) How can I correctly restrict the search to just the top level nodes?
Question 2) How would I then proceed and store the whole thing into a class in the most efficient way? I've tried copypasting the full XML response to websites like http://xmltocsharp.azurewebsites.net/ but all of them seem to be struggling with the duplicate name "ITEM" and basically create one class that has merged ALL of the different "ITEM" child nodes within. Is it possible to fill a List of items in a simple and efficient way? I was hoping to have essentially something along the lines of:
List<DataItem> grd_list = doc.Descendants(XName.Get("ITEM"))
.Select(f => new DataItem()
{
DATA1 = f.Element(XName.Get("DATA1")).Document.ToString(),
DATA2 = f.Element(XName.Get("DATA2")).Document.ToString(),
//etc.....
}).ToList();
but I'm aware that the above LINQ would have to be more complex than this since DATA1 etc also needs to have child nodes.
you could use serializer XML (its just an example following your xml)
you could avoid the problem of item by defining all cases in the definition of the class ITEM. So the items not presents (G,H,I or DATA1,DATA2.. will be null).
using System.Xml.Serialization;
XmlSerializer serializer = new XmlSerializer(typeof(PARENT));
using (StringReader reader = new StringReader(xml))
{
var test = (PARENT)serializer.Deserialize(reader);
}
:
:
[XmlRoot(ElementName = "DATA1")]
public class DATA1
{
[XmlElement(ElementName = "A")]
public string A { get; set; }
[XmlElement(ElementName = "B")]
public string B { get; set; }
[XmlElement(ElementName = "C")]
public string C { get; set; }
}
[XmlRoot(ElementName = "DATA2")]
public class DATA2
{
[XmlElement(ElementName = "D")]
public string D { get; set; }
[XmlElement(ElementName = "E")]
public string E { get; set; }
[XmlElement(ElementName = "F")]
public string F { get; set; }
}
[XmlRoot(ElementName = "ITEM")]
public class ITEM
{
[XmlElement(ElementName = "G")]
public string G { get; set; }
[XmlElement(ElementName = "H")]
public string H { get; set; }
[XmlElement(ElementName = "I")]
public string I { get; set; }
[XmlElement(ElementName = "DATA1")]
public DATA1 DATA1 { get; set; }
[XmlElement(ElementName = "DATA2")]
public DATA2 DATA2 { get; set; }
[XmlElement(ElementName = "DATA3")]
public DATA3 DATA3 { get; set; }
}
[XmlRoot(ElementName = "DATA3")]
public class DATA3
{
[XmlElement(ElementName = "ITEM")]
public List<ITEM> ITEM { get; set; }
}
[XmlRoot(ElementName = "PARENT")]
public class PARENT
{
[XmlElement(ElementName = "ITEM")]
public List<ITEM> ITEM { get; set; }
}
hoping someone can point out what I'm missing here. I'm trying to deserialize the result of a SOAP API call.
I have the following InnerXML within the soapenv:Body of the result
<ns2:OperationResponseInfo xmlns:ns2="http://www.XXXXX.com/webservice/">
<status>
<statusCode>0</statusCode>
<statusDesc>No Error</statusDesc>
</status>
<result>
<record>
<param>
<name>totalrecord</name>
<value>9</value>
</param>
</record>
</result>
<result>
<record>
<param>
<name>ALARMID</name>
<value>1581807719208</value>
</param>
<param>
<name>ALARMDESC</name>
<value>xxxxxxxxxxxxxx</value>
</param>
<param>
<name>ALARMSTATUS</name>
<value>Unacknowledged</value>
</param>
</record>
</result>
</ns2:OperationResponseInfo>
I am trying to deserialize to the following objects :-
[XmlRoot(ElementName = "OperationResponseInfo", Namespace = "http://www.XXXXX.com/webservice/")]
public class OperationResponseInfo
{
[XmlElement(ElementName = "status")]
public Status Status { get; set; }
[XmlElement(ElementName = "result")]
public List<Result> Result { get; set; }
//[XmlAttribute(AttributeName = "ns2", Namespace = "http://www.XXXXX.com/webservice/")]
//public string Ns2 { get; set; }
}
[XmlRoot(ElementName = "status")]
public class Status
{
[XmlElement(ElementName = "statusCode")]
public string StatusCode { get; set; }
[XmlElement(ElementName = "statusDesc")]
public string StatusDesc { get; set; }
}
[XmlRoot(ElementName = "result")]
public class Result
{
[XmlElement(ElementName = "record")]
public List<Record> Record { get; set; }
}
[XmlRoot(ElementName = "record")]
public class Record
{
[XmlElement(ElementName = "param")]
public List<Param> Param { get; set; }
}
[XmlRoot(ElementName = "param")]
public class Param
{
[XmlElement(ElementName = "name")]
public string Name { get; set; }
[XmlElement(ElementName = "value")]
public string Value { get; set; }
}
This is the deserialize method I am using
public class Serializer
{
public T Deserialize<T>(string input) where T : class
{
System.Xml.Serialization.XmlSerializer ser = new System.Xml.Serialization.XmlSerializer(typeof(T));
using (StringReader sr = new StringReader(input))
{
return (T)ser.Deserialize(sr);
}
}
and this is the call to the Deserialize method
var alarmList = ser.Deserialize<OperationResponseInfo>(docInner.InnerXml);
I have this working on another system (with different SOAP result format/c# classes etc.) but for some reason the Deserialize is returning 0 results. The code desn't error at all. What am I missing ?
Namespaces and inheritance rules between xmlns=..., namespace aliases, and what XmlSerializer assumes; try:
[XmlElement(ElementName = "status", Namespace = "")]
public Status Status { get; set; }
[XmlElement(ElementName = "result", Namespace = "")]
public List<Result> Result { get; set; }
More specifically:
when using xmlns="...", namespaces are inherited by descendants
when using ns2:blah, that prefix is not inherited by descendants - it must be explicit
so <status> and <result> are in the empty namespace
but XmlSerializer assumes that namespaces are inherited by default, so [XmlElement(ElementName = "status")] means "an element called status in the same namespace as the current context, i.e. http://www.XXXXX.com/webservice/"
so you need to explicitly tell XmlSerializer to go back to the empty namespace
I have this XML structure:
<?xml version="1.0" encoding="UTF-8" ?>
<response uri="/api/" action="EXPORT">
<result>
<rows>
<row>
<column name="Name1">Value1</column>
<column name="Name2">Value2</column>
</row>
<row>
<column name="Name1">Value1</column>
<column name="Name2">Value2</column>
</row>
</rows>
</result>
</response>
I'm trying to deserialize the XML into a list object like so:
List<ModelXML> model;
using (TextReader reader = new StringReader(xml_str))
{
System.Xml.Serialization.XmlSerializer deserializer = new System.Xml.Serialization.XmlSerializer(typeof(List<ModelXML>),
new XmlRootAttribute("rows"));
model= (List<ModelXML>)deserializer.Deserialize(reader);
}
My parameters in the ModelXML Class:
[XmlElement("Name1")]
public string Name1{ set; get; }
[XmlElement("Name2")]
public string Name2{ set; get; }
And finally I'm getting this error:
The '=' character, hexadecimal value 0x3D, cannot be included in a
name. Line 1, position 13.
What I'm doing wrong? thanks.
Here how you can solve your issue
First you have to change your model
[XmlRoot(ElementName = "column")]
public class Column
{
[XmlAttribute(AttributeName = "name")]
public string Name { get; set; }
[XmlText]
public string Text { get; set; }
}
[XmlRoot(ElementName = "row")]
public class Row
{
[XmlElement(ElementName = "column")]
public List<Column> Column { get; set; }
}
[XmlRoot(ElementName = "rows")]
public class Rows
{
[XmlElement(ElementName = "row")]
public List<Row> Row { get; set; }
}
[XmlRoot(ElementName = "result")]
public class Result
{
[XmlElement(ElementName = "rows")]
public Rows Rows { get; set; }
}
[XmlRoot(ElementName = "response")]
public class Response
{
[XmlElement(ElementName = "result")]
public Result Result { get; set; }
[XmlAttribute(AttributeName = "uri")]
public string Uri { get; set; }
[XmlAttribute(AttributeName = "action")]
public string Action { get; set; }
}
Then
use the deserialization code like the following
//here I'm trying to load the file from the disk but you can do the same by passing a string
Response model;
var xml = File.ReadAllText("file.xml");
using (TextReader reader = new StringReader(xml))
{
System.Xml.Serialization.XmlSerializer deserializer = new System.Xml.Serialization.XmlSerializer(typeof(Response));
model = (Response)deserializer.Deserialize(reader);
}
More you can access your rows like this
var rows = model.Result.Rows;
//hope this can help
When I run your code I get the following exception.
System.InvalidOperationException: 'There is an error in XML document (2, 2).'
InnerException
InvalidOperationException: < response xmlns='' > was not expected.
This is because you must define the outer most element as the root element and not one deeper in the structure like your tried to do. One way to solve this is by defining all nessesary model classes like #BRAHIM Kamel did.
Ïf you don't want to have so many model classes and you don't care to much about performance you could also do this.
[XmlRoot("row")]
public class ModelXML
{
public class Column
{
[XmlAttribute("name")]
public string Name { set; get; }
[XmlText]
public string Value { set; get; }
}
[XmlElement("column")]
public List<Column> Columns { get; set; }
}
IEnumerable<T> Deserialize<T>(IEnumerable<XElement> elements)
{
foreach (var element in elements)
{
using (var reader = XDocument.Parse(element.ToString()).CreateReader())
{
XmlSerializer deserializer = new XmlSerializer(typeof(T));
yield return (T)deserializer.Deserialize(reader);
}
}
}
var document = XDocument.Parse(xml_str);
var collection = Deserialize<ModelXML>(
document.XPathSelectElements("response/result/rows/row"));
I´m trying to deserialize this XML to objects in C# .NET 4.5:
<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"
xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/">
<item id="28" parentID="19" restricted="1">
<dc:creator>Alicia Keys</dc:creator>
<dc:date>2003-01-01</dc:date>
<dc:title>Gangsta Lovin' (feat. Alicia Keys)</dc:title>
</item>
</DIDL-Lite>
Code:
I´m not getting any "item" Lists. The object isn't deserialized.
MemoryStream reader = new MemmoryStream(System.Text.Encoding.Unicode.GetBytes(Result));
var ser = new XmlSerializer(typeof(DIDLLite));
DIDLLite device = (DIDLLite)ser.Deserialize(reader);
Class DIDLLite:
[XmlRoot("DIDL-Lite", Namespace = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/")]
public class DIDLLite {
DIDLLite() {
this.serviceItem = new List<ContainerItem>();
}
[System.Xml.Serialization.XmlArrayItem("item", typeof(ServiceListTypeService), IsNullable = false)]
List<ContainerItem> serviceItem = new List<ContainerItem>();
}
Class ContainerItem:
public class ContainerItem
{
[System.Xml.Serialization.XmlAttribute("id")]
public string id { get; set; }
[System.Xml.Serialization.XmlAttribute("parentID")]
public string parentID { get; set; }
[System.Xml.Serialization.XmlAttribute("restricted")]
public string restricted { get; set; }
[System.Xml.Serialization.XmlAttribute("searchable")]
public string searchable { get; set; }
public string title { get; set; }
}
You have several issues:
you define an XmlArrayItem attribute - but really, in your XML, you don't have any list of items. If you want to use an Xml array construct, you'd need to have something like this for your XML:
<DIDL-Lite .....>
<Items>
<item id="28" parentID="19" restricted="1">
......
</item>
<item id="29" parentID="19" restricted="1">
......
</item>
</Items>
</DIDL-Lite>
So you would need to have an <Items>...</Items> wrapper around your <item> elements.
You have this declaration:
[XmlArrayItem("item", typeof(ServiceListTypeService), IsNullable = false)]
but where is the ServiceListtypeService defined? I don't see any trace of it....
I simplified your code a bit - and this works just fine:
[XmlRoot("DIDL-Lite", Namespace = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/")]
public class DIDLLite
{
[XmlElement("item")]
public ContainerItem Item { get; set; }
}
public class ContainerItem
{
[XmlAttribute("id")]
public string id { get; set; }
[XmlAttribute("parentID")]
public string parentID { get; set; }
[XmlAttribute("restricted")]
public string restricted { get; set; }
[XmlAttribute("searchable")]
public string searchable { get; set; }
// you were missing these elements and their namespace
[XmlElement(Namespace = "http://purl.org/dc/elements/1.1/")]
public string creator { get; set; }
[XmlElement(Namespace = "http://purl.org/dc/elements/1.1/")]
public string date { get; set; }
[XmlElement(Namespace = "http://purl.org/dc/elements/1.1/")]
public string title { get; set; }
}
And now, when I run your code to deserialize your XML, I do get the objects filled nicely.
RestSharp doesn't seem to be paying attention to the "SerializeAs" attribute that I've decorated one of my class names with:
Person
[Serializable, SerializeAs(Name = "person")]
public class Person
{
[SerializeAs(Name = "first-name")]
public string FirstName { get; set; }
[SerializeAs(Name = "contact-data")]
public ContactData ContactData { get; set; }
}
ContactData
public class ContactData
{
[SerializeAs(Name = "email-addresses")]
public List<EmailAddress> EmailAddresses { get; set; }
}
EmailAddress
[SerializeAs(Name = "email-address")]
public class EmailAddress
{
[SerializeAs(Name = "address")]
public string Address { get; set; }
[SerializeAs(Name = "location")]
public string Location { get; set; }
}
I'm using the following code to serialize the XML:
var request = new RestRequest("people/{id}.xml", Method.PUT);
request.AddParameter("id", person.Id, ParameterType.UrlSegment);
request.XmlSerializer = new XmlSerializer();
request.AddBody(person);
However, the resulting XML looks like this:
<person>
<first-name>Scott</first-name>
<contact-data>
<email-adresses>
<EmailAddress>
<address>my#email.com</address>
<location>Work</location>
</EmailAddress>
</email-adresses>
</contact-data>
</person>
You can see that the <EmailAddress> element appears to be ignoring the SerializeAs attribute and is not serialized to "email-address" as I would expect it to be while all of the others work just fine. Does anyone know why this might be happening or how to fix it?
After poking around the RestSharp source, I realized this is a bug in the XmlSerializer built into RestSharp. I've fixed it and submitted a pull request.