C# Parsing an XML and storing it into a Class - c#

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

Related

.net core deserialize xml response

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

XML Deserialize returning 0 results in c#

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

C# Deserializing a XML to an object List

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

Deserialize XML with multiple namespaces to objects

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&apos; (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.

c# serialize second level element to property

I am trying to create serializable class but I want to map second level element to my property of class. What's the best way of doing this.
Example xml & class
<SearchResult>
<Head>
<Title q="test">My search Result</Title>
</Head>
<Results>
<Result>...</Result>
<Result>...</Result>
<Result>...</Result>
</Results>
</SearchResult>
[Serializable]
[XmlRoot(ElementName = "GSP")]
public class SearchResult
{
**[XmlElement(ElementName=#"Head\Title")]**
public string Title { get; set; }
[XmlArray(ElementName = "Results")]
[XmlArrayItem(ElementName = "Result")]
public List<ResultItem> mySearchResultItems { get; set; }
}
[Serializable]
public class ResultItem
{
...
}
So, In my example I would like to map Title property to <Head><Title> text value in xml
Thanks for your help!!
You can't do that. You need to create another class for the <Head> element
[XmlRoot(ElementName = "GSP")]
public class SearchResult
{
[XmlElement(ElementName = "Head")]
public Head Head { get; set; }
[XmlArray(ElementName = "Results")]
[XmlArrayItem(ElementName = "Result")]
public List<ResultItem> mySearchResultItems { get; set; }
}
public class Head
{
[XmlElement(ElementName = "Title")]
public string Title { get; set; }
}
public class ResultItem
{
...
}
Also, if the Title element must have an attribute, you will also need to create a new class for the Title element...
By the way, the [Serializable] attribute has nothing to do with XML serialization...
You don't need custom serialization. Thomas Levesque is correct, but you can get what you want using the same design approach you employed for the Results.
Example:
[XmlRoot(ElementName = "GSP")]
public class SearchResult
{
//public string Title { get; set; }
[XmlArray(ElementName = "Header")]
[XmlArrayItem(ElementName = "Title")]
public List<String> myHeaderItems { get; set; }
[XmlArray(ElementName = "Results")]
[XmlArrayItem(ElementName = "Result")]
public List<ResultItem> mySearchResultItems { get; set; }
public SearchResult()
{
myHeaderItems = new List<String>();
mySearchResultItems= new List<ResultItem>();
}
public SearchResult(string title) : this()
{
myHeaderItems.Add(title);
}
}
public class ResultItem
{
[XmlText]
public String Flavor;
}
public void Run()
{
SearchResult sr = new SearchResult("Search1");
sr.mySearchResultItems.Add(new ResultItem() {Flavor = "one" }) ;
sr.mySearchResultItems.Add(new ResultItem() {Flavor = "two" }) ;
var s1 = new XmlSerializer(typeof(SearchResult));
Console.WriteLine("Serialized:\n{0}", s1.SerializeToString(sr));
}
Produces this output:
<GSP>
<Header>
<Title>Search1</Title>
</Header>
<Results>
<Result>one</Result>
<Result>two</Result>
</Results>
</GSP>

Categories