deserialize xml into inherited classes from base class - c#

I have the following xml structure:
<Root1>
<name>Name1</name>
<company>Comp1</company>
<url>site.com</url>
<elements>
<element id="12" type="1">
<url>site1.com</url>
<price>15000</price>
...
<manufacturer_warranty>true</manufacturer_warranty>
<country_of_origin>Япония</country_of_origin>
</element>
<element id="13" type="2">
<url>site2.com</url>
<price>100</price>
...
<language>lg</language>
<binding>123</binding>
</element>
</elements>
</Root1>
I need to deserialize this xml into an object. You can see the element contains some equals field: url and price.
I would like to move these fields into a parent class and then inherit this class from other classes.
I created the class Root1:
namespace app1
{
[Serializable]
public class Root1
{
[XmlElement("name")]
public string Name { get; set; }
[XmlElement("company")]
public string Company { get; set; }
[XmlElement("url")]
public string Url { get; set; }
[XmlElement("elements")]
public List<Element> ElementList { get; set; }
}
}
and then I created base class for Element:
[Serializable]
public class Element
{
[XmlElement("url")]
public string Url { get; set; }
[XmlElement("price")]
public string Price { get; set; }
}
and then I inherited this class from other classes:
[Serializable]
public class Element1 : Element
{
[XmlElement("manufacturer_warranty")]
public string mw { get; set; }
[XmlElement("country_of_origin")]
public string co { get; set; }
}
[Serializable]
public class Element2 : Element
{
[XmlElement("language")]
public string lg { get; set; }
[XmlElement("binding")]
public string bind { get; set; }
}
When I deserialize this xml to object Root1 I get the object - it is ok.
But the List of Elements contains only Element objects not Element1 and Element2 objects.
How I do deserialize this xml so list of Elements contains Element1 and Element2 objects?

#netwer It is not working for you because the code suggested above generates the xml (below) that does not match with the one you use for deserialization (see how it specifies derived types in for element).
<?xml version="1.0" encoding="utf-8"?>
<Root1>
<name>Name1</name>
<company>Comp1</company>
<url>site.com</url>
<elements>
<element d3p1:type="Element1" xmlns:d3p1="http://www.w3.org/2001/XMLSchema-instance">
<url>site1.com</url>
<price>15000</price>
<manufacturer_warranty>true</manufacturer_warranty>
<country_of_origin>Япония</country_of_origin>
</element>
<element d3p1:type="Element2" xmlns:d3p1="http://www.w3.org/2001/XMLSchema-instance">
<url>site2.com</url>
<price>100</price>
<language>lg</language>
<binding>123</binding>
</element>
</elements>
</Root1>
So you will either have to match this format with source xml (change code or API that returns this xml data) or take another approach. Even if you manage to do with former one you have to find a way to access Element1 or Element2 specific properties.
newRoot1.Elements.ElementList[i] will always let you access only price and url since your list is of Element type. Although run time type of ElementList[i] will be Element1 or Element2, how will you detect that?
Here I suggest alternative approach. Irrespective of whether your application (client) generates this xml or the server which returns it on hitting API, you should be able to gather information on all the fields applicable to an 'element' object. If it is your code you know it already, if it is an API there must be a document for it. This way you only have to create one 'Element' (no derived classes) and put proper checks (mostly string.IsNullOrEmpty()) before accessing Element class property values in your code. Only the properties that are present in your xml 'element' element will be considered rest will be set to NULL for that instance.
[Serializable]
public class Element
{
[XmlElement("url")]
public string Url { get; set; }
[XmlElement("price")]
public string Price { get; set; }
[XmlElement("manufacturer_warranty")]
public string mw { get; set; }
[XmlElement("country_of_origin")]
public string co { get; set; }
[XmlElement("language")]
public string lg { get; set; }
[XmlElement("binding")]
public string bind { get; set; }
}

I think you should use XmlIncludeAttribute like this:
[XmlInclude(typeof(Element1))]
public class Element
{
}

Here is xml and code. I like to first serialize with test data, then deserialize.
<?xml version="1.0" encoding="utf-8"?>
<Root1>
<name>Name1</name>
<company>Comp1</company>
<url>site.com</url>
<element d2p1:type="Element1" xmlns:d2p1="http://www.w3.org/2001/XMLSchema-instance">
<url>site1.com</url>
<price>15000</price>
<manufacturer_warranty>true</manufacturer_warranty>
<country_of_origin>Япония</country_of_origin>
</element>
<element d2p1:type="Element2" xmlns:d2p1="http://www.w3.org/2001/XMLSchema-instance">
<url>site2.com</url>
<price>100</price>
<language>lg</language>
<binding>123</binding>
</element>
</Root1>
Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string FILENAME = #"c:\temp\test.xml";
Root1 root1 = new Root1() {
Name = "Name1",
Company = "Comp1",
Url = "site.com",
ElementList = new List<Element>() {
new Element1() {
Url = "site1.com",
Price = "15000",
mw = "true",
co = "Япония"
},
new Element2() {
Url = "site2.com",
Price = "100",
lg = "lg",
bind = "123"
}
}
};
XmlSerializer serializer = new XmlSerializer(typeof(Root1));
StreamWriter writer = new StreamWriter(FILENAME);
XmlSerializerNamespaces _ns = new XmlSerializerNamespaces();
_ns.Add("", "");
serializer.Serialize(writer, root1, _ns);
writer.Flush();
writer.Close();
writer.Dispose();
XmlSerializer xs = new XmlSerializer(typeof(Root1));
XmlTextReader reader = new XmlTextReader(FILENAME);
Root1 newRoot1 = (Root1)xs.Deserialize(reader);
}
}
[XmlRoot("Root1")]
public class Root1
{
[XmlElement("name")]
public string Name { get; set; }
[XmlElement("company")]
public string Company { get; set; }
[XmlElement("url")]
public string Url { get; set; }
[XmlElement("element")]
public List<Element> ElementList { get; set; }
}
[XmlInclude(typeof(Element1))]
[XmlInclude(typeof(Element2))]
[XmlRoot("element")]
public class Element
{
[XmlElement("url")]
public string Url { get; set; }
[XmlElement("price")]
public string Price { get; set; }
}
[XmlRoot("element1")]
public class Element1 : Element
{
[XmlElement("manufacturer_warranty")]
public string mw { get; set; }
[XmlElement("country_of_origin")]
public string co { get; set; }
}
[XmlRoot("element2")]
public class Element2 : Element
{
[XmlElement("language")]
public string lg { get; set; }
[XmlElement("binding")]
public string bind { get; set; }
}
}

Code below matches better with your posted XML. You need to compare the generated xml with your xml.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string FILENAME = #"c:\temp\test.xml";
Root1 root1 = new Root1()
{
Name = "Name1",
Company = "Comp1",
Url = "site.com",
cElement = new Elements() {
ElementList = new List<Element>() {
new Element1() {
Url = "site1.com",
Price = "15000",
mw = "true",
co = "Япония"
},
new Element2() {
Url = "site2.com",
Price = "100",
lg = "lg",
bind = "123"
}
}
}
};
XmlSerializer serializer = new XmlSerializer(typeof(Root1));
StreamWriter writer = new StreamWriter(FILENAME);
XmlSerializerNamespaces _ns = new XmlSerializerNamespaces();
_ns.Add("", "");
serializer.Serialize(writer, root1, _ns);
writer.Flush();
writer.Close();
writer.Dispose();
XmlSerializer xs = new XmlSerializer(typeof(Root1));
XmlTextReader reader = new XmlTextReader(FILENAME);
Root1 newRoot1 = (Root1)xs.Deserialize(reader);
}
}
[XmlRoot("Root1")]
public class Root1
{
[XmlElement("name")]
public string Name { get; set; }
[XmlElement("company")]
public string Company { get; set; }
[XmlElement("url")]
public string Url { get; set; }
[XmlElement("elements")]
public Elements cElement { get; set; }
}
[XmlRoot("elements")]
public class Elements
{
[XmlElement("element")]
public List<Element> ElementList { get; set; }
}
[XmlInclude(typeof(Element1))]
[XmlInclude(typeof(Element2))]
[XmlRoot("element", Namespace = "")]
public class Element
{
[XmlElement("url")]
public string Url { get; set; }
[XmlElement("price")]
public string Price { get; set; }
}
[XmlRoot("element1", Namespace = "")]
public class Element1 : Element
{
[XmlElement("manufacturer_warranty")]
public string mw { get; set; }
[XmlElement("country_of_origin")]
public string co { get; set; }
}
[XmlRoot("element2", Namespace = "")]
public class Element2 : Element
{
[XmlElement("language")]
public string lg { get; set; }
[XmlElement("binding")]
public string bind { get; set; }
}
}​

Related

There is an error in XML document (2, 2) when desearlizing xml to c# object

XML
<?xml version="1.0" encoding="UTF-8"?>
<orgc:Organizations xmlns:orgc="urn:workday.com/connector/orgs" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<orgc:Organization>
<orgc:Organization_ID>SR1Code34</orgc:Organization_ID>
<orgc:Organization_Code>SR1Code34</orgc:Organization_Code>
<orgc:Organization_Type>Cost_Center_Hierarchy</orgc:Organization_Type>
<orgc:Organization_Name>LTL Services</orgc:Organization_Name>
<orgc:Organization_Description>LTL Services</orgc:Organization_Description>
<orgc:Organization_Subtype>ORGANIZATION_SUBTYPE-3-20</orgc:Organization_Subtype>
<orgc:Inactive>false</orgc:Inactive>
<orgc:Superior_Organization>DL2Code11</orgc:Superior_Organization>
</orgc:Organization>
<orgc:Organization>
<orgc:Organization_ID>SR1Code35</orgc:Organization_ID>
<orgc:Organization_Code>SR1Code35</orgc:Organization_Code>
<orgc:Organization_Type>Cost_Center_Hierarchy</orgc:Organization_Type>
<orgc:Organization_Name>Consolidation</orgc:Organization_Name>
<orgc:Organization_Description>Consolidation</orgc:Organization_Description>
<orgc:Organization_Subtype>ORGANIZATION_SUBTYPE-3-20</orgc:Organization_Subtype>
<orgc:Inactive>false</orgc:Inactive>
<orgc:Superior_Organization>DL2Code11</orgc:Superior_Organization>
</orgc:Organization>
</orgc:Organizations>
Class
[XmlRoot(ElementName = "Organizations", Namespace = "urn: workday.com/connector/orgs", IsNullable = true )]
public class CostCenterHierarchy
{
[XmlElement("orgc:Organization_ID")]
public string CostCenterHierarchyId { get; set; }
[XmlElement("orgc:Organization_Code")]
public string Code { get; set; }
[XmlElement("orgc:Organization_Name")]
public string Name { get; set; }
[XmlElement("orgc:Organization_Description")]
public string Description { get; set; }
[XmlElement("orgc:Organization_Subtype")]
public string Subtype { get; set; }
[XmlElement("orgc:Superior_Organization")]
public string ParentHierarchyId { get; set; }
}
Method to deseralize xml to c# class
private List<CostCenterHierarchy> ProcessCostCenterHierarchy(string filePath)
{
var costCenterHierarchyList = new List<CostCenterHierarchy>();
//var costCenterHierarchy = new CostCenterHierarchy();
XmlSerializer xmlSerializer = new XmlSerializer(typeof(List<CostCenterHierarchy>));
using (var reader = XmlReader.Create(filePath))
{
var test = xmlSerializer.Deserialize(reader);
}
return costCenterHierarchyList;
}
Error Message
Message = "There is an error in XML document (2, 2)."
InnerException = {"<Organizations xmlns='urn:workday.com/connector/orgs'> was not expected."}
I'm not sure where I am going wrong. Seems like it should be pretty easy but I've played around with this and keep getting the same error message. Any help would be much appreciated.
The code below works. You have an array and serialization doesn't like a list as the type. The Url "urn:workday.com/connector/orgs" the serializaer doesn't like and had to replace the "urn:" with "http://workday.com/connector/orgs".
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(FILENAME);
XmlSerializer serializer = new XmlSerializer(typeof(Organizations));
Organizations organizations = (Organizations)serializer.Deserialize(reader);
}
}
[XmlRoot(ElementName = "Organizations", Namespace = "http://workday.com/connector/orgs")]
public class Organizations
{
[XmlElement(ElementName = "Organization", Namespace = "http://workday.com/connector/orgs")]
public List<CostCenterHierarchy> organizations { get; set; }
}
public class CostCenterHierarchy
{
[XmlElement("Organization_ID")]
public string CostCenterHierarchyId { get; set; }
[XmlElement("Organization_Code")]
public string Code { get; set; }
[XmlElement("Organization_Name")]
public string Name { get; set; }
[XmlElement("Organization_Description")]
public string Description { get; set; }
[XmlElement("Organization_Subtype")]
public string Subtype { get; set; }
[XmlElement("Superior_Organization")]
public string ParentHierarchyId { get; set; }
}
}

How to de-serialise a list with multiple xsi:type

I'm using the XmlSerializer in System.Xml.Serialization.
I have a list (or two lists really) separated by xsi:type.
<?xml version="1.0" encoding="utf-8"?>
<ButikerOmbud xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Info>
<Meddelande>blah blah</Meddelande>
</Info>
<ButikOmbud xsi:type="StoreAssortmentViewModel">
<Typ>Butik</Typ><Nr>2515</Nr>
</ButikOmbud>
<ButikOmbud xsi:type="StoreAssortmentViewModel">
<Typ>Butik</Typ><Nr>2516</Nr>
</ButikOmbud>
<ButikOmbud xsi:type="AgentAssortmentViewModel">
<Typ>Ombud</Typ><Nr>011703-91A</Nr>
</ButikOmbud>
<ButikOmbud xsi:type="AgentAssortmentViewModel">
<Typ>Ombud</Typ><Nr>011703-92B</Nr>
</ButikOmbud>
</ButikerOmbud>
I've created some classes that map to this:
[XmlRoot(ElementName = "ButikerOmbud")]
public class ButiksCollection
{
[XmlElement(ElementName = "Info")]
public Info Info { get; set; }
[XmlElement(ElementName = "ButikOmbud")]
public List<Butik> Butiker { get; set; }
}
[XmlRoot(ElementName = "ButikOmbud")]
[XmlType(TypeName = "StoreAssortmentViewModel")]
public class Butik
{
[XmlElement(ElementName = "Typ")]
public string Typ { get; set; }
[XmlElement(ElementName = "Nr")]
public int Nr { get; set; }
}
and then I'll do this
(ButiksCollection)(new XmlSerializer(typeof(ButiksCollection)).Deserialize(memoryStream));
This should work if only the StoreAssortmentViewModel existed. But given that there exists AgentAssortmentViewModel under the same node. I'm not sure how I should de-serialise this. I'm assuming there should be another collection List<Butik> Agents on ButiksCollection.
The only attribute I've found that seems to map to xsi:type is applied to classes, which I don't think is what I want here
How do I arrange and attribute my classes so this will de-serialize?
Here's all that on dotnetfiddle: https://dotnetfiddle.net/vmT4SK
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(FILENAME);
XmlSerializer serializer = new XmlSerializer(typeof(ButiksCollection));
ButiksCollection butik = (ButiksCollection)serializer.Deserialize(reader);
List<StoreAssortmentViewModel> storeAssortments = butik.Butiker.Where(x => x.GetType() == typeof(StoreAssortmentViewModel)).Select(x => (StoreAssortmentViewModel)x).ToList();
List<AgentAssortmentViewModel> agentAssortments = butik.Butiker.Where(x => x.GetType() == typeof(AgentAssortmentViewModel)).Select(x => (AgentAssortmentViewModel)x).ToList();
}
}
[XmlRoot(ElementName = "ButikerOmbud")]
public class ButiksCollection
{
[XmlElement(ElementName = "Info")]
public Info Info { get; set; }
[XmlElement(ElementName = "ButikOmbud")]
public List<Butik> Butiker { get; set; }
}
[XmlRoot(ElementName = "ButikOmbud")]
[XmlInclude(typeof(StoreAssortmentViewModel))]
[XmlInclude(typeof(AgentAssortmentViewModel))]
public class Butik
{
[XmlElement(ElementName = "Typ")]
public string Typ { get; set; }
[XmlElement(ElementName = "Nr")]
public string Nr { get; set; }
}
public class Info
{
public string Meddelande { get; set; }
}
public class StoreAssortmentViewModel : Butik
{
}
public class AgentAssortmentViewModel : Butik
{
}
}

Deserialize XML with XmlSerializer where parent and child have the same tag

I'm having difficulty parsing an XML string where the parent and child nodes have the same tag name. Obviously, I could replace the open/close tags with empty strings and parse with the code below, but that's not elegant.
I've searched and see that there are answers for how to do this with XDocument, but I specifically would like to do this with XmlSerializer (if possible).
Below is a minimal, reproducable example.
Example XML:
<AddJob>
<AddJob RequestStatus="OK" RequestMessage="Job successfuly added [testPrintServer.tif, PES_Carpet_16C_76.2 x 50.8 dpi_170517_Normal]" UUID="74ad5971-7baf-49ce-b85b-ee08188d5721" />
</AddJob>
Parsing code:
public class XmlHelper
{
public static T Deserialize<T>(string xml)
{
var serializer = new XmlSerializer(typeof(T));
T result;
using (var reader = new StringReader(xml))
{
result = (T)serializer.Deserialize(reader);
}
return result;
}
}
Data model:
[XmlRoot("AddJob")]
public class AddJob
{
[XmlAttribute]
public string RequestStatus { get; set; }
[XmlAttribute]
public string RequestMessage { get; set; }
[XmlAttribute("UUID")]
public string RipJobId { get; set; }
}
Calling code:
var addedJobResponse = XmlHelper.Deserialize<AddJob>(exampleXml);
Your data model doesn't match your xml structure.
Please use something like that:
[XmlRoot("AddJob")]
public class AddJob
{
[XmlElement(ElementName = "AddJob")]
public List<NestedAddJob> AddJobs { get; set; }
}
public class NestedAddJob
{
[XmlAttribute]
public string RequestStatus { get; set; }
[XmlAttribute]
public string RequestMessage { get; set; }
[XmlAttribute("UUID")]
public string RipJobId { get; set; }
}
The nested AddJob elements look like an array and you cannot have an array at the root. So add a Root class like code below :
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace ConsoleApplication75
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
string xml = "<AddJob>" +
"<AddJob RequestStatus=\"OK\" RequestMessage=\"Job successfuly added [testPrintServer.tif, PES_Carpet_16C_76.2 x 50.8 dpi_170517_Normal]\" UUID=\"74ad5971-7baf-49ce-b85b-ee08188d5721\" />" +
"</AddJob>";
Root job = XmlHelper.Deserialize<Root>(xml);
}
}
public class XmlHelper
{
public static T Deserialize<T>(string xml)
{
var serializer = new XmlSerializer(typeof(T));
T result;
using (var reader = new StringReader(xml))
{
result = (T)serializer.Deserialize(reader);
}
return result;
}
}
[XmlRoot("AddJob")]
public class Root
{
public AddJob AddJob { get; set; }
}
public class AddJob
{
[XmlAttribute]
public string RequestStatus { get; set; }
[XmlAttribute]
public string RequestMessage { get; set; }
[XmlAttribute("UUID")]
public string RipJobId { get; set; }
}
}

C# XML reading foreach always first value

My XML
<?xml version="1.0" encoding="UTF-8"?>
<teklif>
<bilgiler>
<firma>Firma Adı</firma>
<aciklama>Açıklama</aciklama>
<isim>Ad Soyad</isim>
<telefon>Telefon</telefon>
<eposta>E Posta</eposta>
<urunler>
<urun>
<resimDosyasi>Dosya Seçilmedi</resimDosyasi>
<aciklama>Ürün Açıklaması</aciklama>
<birim>3,00</birim>
<miktar>1</miktar>
<toplam>0,00</toplam>
</urun>
<urun>
<resimDosyasi>Dosya Seçilmedi</resimDosyasi>
<aciklama>Ürün Açıklaması</aciklama>
<birim>5,00</birim>
<miktar>1</miktar>
<toplam>0,00</toplam>
</urun>
<urun>
<resimDosyasi>Dosya Seçilmedi</resimDosyasi>
<aciklama>aas</aciklama>
<birim>2,00</birim>
<miktar>1</miktar>
<toplam>0,00</toplam>
</urun>
</urunler>
And My Function which reads the XML file
XmlDocument doc = new XmlDocument();
doc.Load(filename);
XmlNodeList xmllist = doc.SelectNodes("/teklif/bilgiler/urunler");
foreach(XmlNode nod in xmllist)
{
foreach(XmlNode childNode in nod.ChildNodes)
{
// her ürünün childnode oldu
if(childNode.Name == "#text")
{
} else
{
var urun_resim = childNode.SelectSingleNode("//resimDosyasi").InnerText;
var urun_aciklama = childNode.SelectSingleNode("//aciklama").InnerText;
var urun_birim = childNode.SelectSingleNode("//birim").InnerText;
MessageBox.Show(urun_birim);
var urun_miktar = childNode.SelectSingleNode("//miktar").InnerText;
var urun_toplam = childNode.SelectSingleNode("//toplam").InnerText;
var urun = new Urun(urun_resim, urun_birim, urun_miktar, urun_aciklama);
lw_urunler.Items.Add(urun);
}
}
}
The problem is when I message box the //birim in foreach loop, it always writes the first one - 3,00((3 times). As you can see in XML, first is 3,00, the second is 5,00 and the third is 2,00 but it always writes the first one. I checked up a lot but I can't see the problem.
Try it without the //, e.g. childNode.SelectSingleNode("birim"). Two forward slashes mean the root of the XML document, and my guess is it's just always finding the first birim node starting from the root each time.
The // means to select nodes no matter where they are under the current context. Current context defaults to the root. To limit the results to those under the current child node follow this pattern (just add a dot):
var urun_resim = childNode.SelectSingleNode(".//resimDosyasi").InnerText;
Using xml linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication52
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
List<urun> uruns = doc.Descendants("urun").Select(x => new urun() {
resimDosyasi = (string)x.Element("resimDosyasi"),
aciklama = (string)x.Element("aciklama"),
birim = (string)x.Element("birim"),
miktar = (int)x.Element("miktar"),
toplam = (string)x.Element("toplam")
}).ToList();
}
}
public class urun
{
public string resimDosyasi { get; set; }
public string aciklama { get; set; }
public string birim { get; set; }
public int miktar { get; set; }
public string toplam { get; set; }
}
}
Another approach that would increase the readability and maintenance of your code would be to map the XML schema into an object model.
Doing so, you could easly load the XML data as follows:
XmlSerializer serializer = new XmlSerializer(typeof(Teklif));
using (FileStream fileStream = new FileStream(filename, FileMode.Open))
{
Teklif result = (Teklif)serializer.Deserialize(fileStream);
}
// Object model example
[XmlRoot("teklif")]
public class Teklif
{
[XmlElement()]
public bilgiler bilgiler { get; set; }
}
public class bilgiler
{
[XmlElement()] public string firma { get; set; }
[XmlElement()] public string aciklama { get; set; }
[XmlElement()] public string isim { get; set; }
[XmlElement()] public string telefon { get; set; }
[XmlElement()] public string eposta { get; set; }
[XmlArray()]
[XmlArrayItem("urun")]
public List<Urun> urunler { get; set; }
}
public class Urun
{
[XmlElement()] public string resimDosyasi { get; set; }
[XmlElement()] public string aciklama { get; set; }
[XmlElement()] public string birim { get; set; }
[XmlElement()] public int miktar { get; set; }
[XmlElement()] public string toplam { get; set; }
}

Unable to get values from XML using xPath C#

I have two XML files, I am able to get data through xPath in c# from first XML which here:
<CONSOLIDATED_LIST xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://www.example/acb.xsd"
dateGenerated="2018-04-19T19:18:41.129-04:00">
<INDIVIDUALS>
<INDIVIDUAL>
<DATAID>2975591</DATAID>
<VERSIONNUM>1</VERSIONNUM>
<FIRST_NAME>ABC</FIRST_NAME>
<SECOND_NAME>XYZ</SECOND_NAME>
<INDIVIDUAL_ALIAS>
<QUALITY>Good</QUALITY>
<ALIAS_NAME>abcd</ALIAS_NAME>
</INDIVIDUAL_ALIAS>
<INDIVIDUAL_ALIAS>
<QUALITY>Bad</QUALITY>
<ALIAS_NAME>ylmn</ALIAS_NAME>
</INDIVIDUAL_ALIAS>
</INDIVIDUAL>
</INDIVIDUALS>
</CONSOLIDATED_LIST>
the way I am getting Data, please also see the comments inside code.
var xml = DownloadString(link); //link refers to XML file on web
e = XElement.Parse(xml);
//parentNodeForPerson = "INDIVIDUAL" for above and "sdnEntry" for below;
var lstIndividuals = e.Descendants(parentNodeForPerson);
Data _individualData;
List<Data> individualList = new List<Data>();
foreach (var individual in lstIndividuals)
{
_individualData = new Data();
//personParentValue1 for above is FIRST_NAME and below is firstName
_individualData.First_Name = individual.Descendants(personParentValue1)
.Any() == true ? individual.Descendants(personParentValue1).First().Value : "";
//personParentValue2 for above is SECOND_NAME and below is lastName
_individualData.Last_Name = individual.Descendants(personParentValue2)
.Any() == true ? individual.Descendants(personParentValue2).First().Value : "";
//childNodeForPersonfor above is INDIVIDUAL_ALIAS and below is aka
var lstIndvidualAlias = individual.Descendants(childNodeForPerson);
_individualData.Alias = new List<string>();
foreach (var alias in lstIndvidualAlias)
{
//personChildValue1 for above is ALIAS_NAME and below is lastName & fisrtName
if (!String.IsNullOrWhiteSpace(alias.Descendants(personChildValue1).First().Value))
{
_individualData.Alias.Add(alias.Descendants(personChildValue1)
.Any() == true ? alias.Descendants(personChildValue1).First().Value : "");
}
}
individualList.Add(_individualData);
}
this is XML where I am not getting data
<List xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://tempuri.org/List.xsd">
<publshInformation>
<Publish_Date>04/18/2018</Publish_Date>
<Record_Count>6299</Record_Count>
</publshInformation>
<sdnEntry>
<lastName>ABCD</lastName>
<fisrtName>XYS</fisrtName>
<akaList>
<aka>
<category>strong</category>
<lastName>ABCDT</lastName>
<fisrtName>XYS</fisrtName>
</aka>
<aka>
<category>Weak</category>
<lastName>AssDT</lastName>
<fisrtName>XYsS</fisrtName>
</aka>
</akaList>
</sdnEntry>
</List>
Edit:
Data Class
public class Data
{
public string Last_Name { get; set; }
public string First_Name { get; set; }
public List<string> Alias { get; set; }
}
SOLUTION
Yes, that's normal and depends on namespaces.
Just edit your LINQ queries and go from the one you used:
var lstIndividuals = e.Descendants(parentNodeForPerson);
to this:
var lstIndividuals = e.Descendants().Where(f => f.Name.LocalName.ToString() == parentNodeForPerson);
And it will work.
LONG EXPLANATION
The above solution basically ignores all namespace information when retrieving elements by name.
The things is that in the first XML you have:
<CONSOLIDATED_LIST xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://www.example/acb.xsd"
dateGenerated="2018-04-19T19:18:41.129-04:00">
Whilst in the second we find
<List xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://tempuri.org/List.xsd">
The second XML has two namespaces defined, which throws your LINQ queries out of whack; in fact, if you just removed this from the second XML
xmlns="http://tempuri.org/List.xsd"
your code would work without any changes.
To start with, you can read the answers to What does “xmlns” in XML mean?, especially this one. In short, namespaces can be useful e.g. when you combine XMLs from different sources and you get elements with the same name: by prepending a namespace, elements with the same name will be seen as different:
<namespaceA:elementName>blah </namespaceA:elementName>
<namespaceB:elementName>blah blah</namespaceB:elementName>
In your XML you have two namespaces but, when looking for Descendants you don't specify which namespace your elements belong to.
Another solution, perhaps more robust than the quick one above, would be specifying the namespace the name of an element belongs to, e.g. like the OP does in this question, using XName.Get: in your case, you would get the nodes of the second XML this way:
var lstIndividuals = e.Descendants(XName.Get(parentNodeForPerson, "http://tempuri.org/List.xsd"));
which works because the URI specified (http://tempuri.org/List.xsd) is the one of the default namespace you declared:
xmlns="http://tempuri.org/List.xsd"
and, since your elements don't have any prefix, it's the namespace your elements belong to; if they belonged to the other namespace you defined, i.e. xsi, their names would be e.g. xsi:sdnEntry.
Of course, since there's no default namespace in your first XML declaration but only the xsi namespace (which refers to a different URI), you would need to pass its URI to XName.Get and adapt your code.
In other words, you should make the URL parametric and pass it to your method, rather than hardcoding it like I did.
I tried your xml(s). I have used xml deserializer and it seems to work fine at my end. Please check the following code:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Reflection;
using System.Xml.Serialization;
using XML1;
using XML2;
namespace ConsoleApplication1
{
public class Program
{
private static void Main(string[] args)
{
var list1 = Deserialize<CONSOLIDATED_LIST>(#"CONSOLIDATED_LIST.xml"); // pass the path to your xml here
var list2 = Deserialize<List>(#"LIST.xml");
}
public static T Deserialize<T>(string path)
{
T obj;
XmlSerializer serializer = new XmlSerializer(typeof(T));
var reader = new StreamReader(path);
obj = (T)serializer.Deserialize(reader);
reader.Close();
return obj;
}
}
}
namespace XML1
{
[XmlRoot(ElementName = "INDIVIDUAL_ALIAS")]
public class INDIVIDUAL_ALIAS
{
[XmlElement(ElementName = "QUALITY")]
public string QUALITY { get; set; }
[XmlElement(ElementName = "ALIAS_NAME")]
public string ALIAS_NAME { get; set; }
}
[XmlRoot(ElementName = "INDIVIDUAL")]
public class INDIVIDUAL
{
[XmlElement(ElementName = "DATAID")]
public string DATAID { get; set; }
[XmlElement(ElementName = "VERSIONNUM")]
public string VERSIONNUM { get; set; }
[XmlElement(ElementName = "FIRST_NAME")]
public string FIRST_NAME { get; set; }
[XmlElement(ElementName = "SECOND_NAME")]
public string SECOND_NAME { get; set; }
[XmlElement(ElementName = "INDIVIDUAL_ALIAS")]
public List<INDIVIDUAL_ALIAS> INDIVIDUAL_ALIAS { get; set; }
}
[XmlRoot(ElementName = "INDIVIDUALS")]
public class INDIVIDUALS
{
[XmlElement(ElementName = "INDIVIDUAL")]
public INDIVIDUAL INDIVIDUAL { get; set; }
}
[XmlRoot(ElementName = "CONSOLIDATED_LIST")]
public class CONSOLIDATED_LIST
{
[XmlElement(ElementName = "INDIVIDUALS")]
public INDIVIDUALS INDIVIDUALS { get; set; }
[XmlAttribute(AttributeName = "xsi", Namespace = "http://www.w3.org/2000/xmlns/")]
public string Xsi { get; set; }
[XmlAttribute(AttributeName = "noNamespaceSchemaLocation", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public string NoNamespaceSchemaLocation { get; set; }
[XmlAttribute(AttributeName = "dateGenerated")]
public string DateGenerated { get; set; }
}
}
namespace XML2
{
[XmlRoot(ElementName = "publshInformation", Namespace = "http://tempuri.org/List.xsd")]
public class PublshInformation
{
[XmlElement(ElementName = "Publish_Date", Namespace = "http://tempuri.org/List.xsd")]
public string Publish_Date { get; set; }
[XmlElement(ElementName = "Record_Count", Namespace = "http://tempuri.org/List.xsd")]
public string Record_Count { get; set; }
}
[XmlRoot(ElementName = "aka", Namespace = "http://tempuri.org/List.xsd")]
public class Aka
{
[XmlElement(ElementName = "category", Namespace = "http://tempuri.org/List.xsd")]
public string Category { get; set; }
[XmlElement(ElementName = "lastName", Namespace = "http://tempuri.org/List.xsd")]
public string LastName { get; set; }
[XmlElement(ElementName = "fisrtName", Namespace = "http://tempuri.org/List.xsd")]
public string FisrtName { get; set; }
}
[XmlRoot(ElementName = "akaList", Namespace = "http://tempuri.org/List.xsd")]
public class AkaList
{
[XmlElement(ElementName = "aka", Namespace = "http://tempuri.org/List.xsd")]
public List<Aka> Aka { get; set; }
}
[XmlRoot(ElementName = "sdnEntry", Namespace = "http://tempuri.org/List.xsd")]
public class SdnEntry
{
[XmlElement(ElementName = "lastName", Namespace = "http://tempuri.org/List.xsd")]
public string LastName { get; set; }
[XmlElement(ElementName = "fisrtName", Namespace = "http://tempuri.org/List.xsd")]
public string FisrtName { get; set; }
[XmlElement(ElementName = "akaList", Namespace = "http://tempuri.org/List.xsd")]
public AkaList AkaList { get; set; }
}
[XmlRoot(ElementName = "List", Namespace = "http://tempuri.org/List.xsd")]
public class List
{
[XmlElement(ElementName = "publshInformation", Namespace = "http://tempuri.org/List.xsd")]
public PublshInformation PublshInformation { get; set; }
[XmlElement(ElementName = "sdnEntry", Namespace = "http://tempuri.org/List.xsd")]
public SdnEntry SdnEntry { 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; }
}
}
I personally would prefer the xml deserializer. It would be easy and more maintainable. Your underlying code, would remain the same and the deserialization can be done base on the Type T in your xml

Categories