Complex XML to C# classes - how to deserialize them? - c#

My goal is to extract the data from the XML. But I have some rather complex XML that is not easily converted to C sharp classes.
The XML looks like this:
<group.........>
<suite....>
<properties>
<property name=....../>
</properties>
<suite type="test">
<suite type="test1">
<suite...>
<suite...>
<suite...>
<case id="1000" name="example">
<properties>
<property ...../>
</properties>
</case>
<case.......>
<properties>
<property ...../>
</properties>
</case>
<case>
<properties>
<property .... />
</properties>
</case>
</suite>
</suite>
</suite>
</suite>
</suite>
</suite>
</group>
I have used an online xml to c sharp convert to create classes, but it does not seem to handle the XML structure correctly.
Update:
The XML comes from NUNIT3. It is the result of the UNIT3 console that is written to an XML document.
Update 2:
I am able to extract data using below code - don't know if there is a more elegant solution:
XElement resultFile = XElement.Load($"{resultFilePathList}");
var dataFromXML = (
from data in resultFile.Descendants("case")
select new
{
caseid = data.Attribute("id").Value,
name = data.Attribute("fullname").Value,
result = data.Attribute("result").Value,
duration = data.Attribute("duration").Value
}
);

Check this code, is as simple as this
using System.Xml.Serialization;
using System.IO;
.......
StreamReader streamer = new StreamReader("yourgroup.xml");
XmlSerializer serializer = new XmlSerializer(typeof(group));
group x = (group)serializer.Deserialize(streamer);
streamer.Close();
And you have do define your classes, somehow like this
public class group {List<suite> suite;}
public class suite
{
public List<suite> suite;
public List<property> properties;
}
Ok, you can add additional annotation if you need specific handling
For instance if there is "case" element, create your class with different name
public class xcase
{
public String id;
public String name;
public Property[] properties;...
}
public class suite
{
[XmlElement(ElementName = "case")]
public xcase[] cases {get; set; }
....
}

Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication120
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement group = doc.Root;
Suite rootSuite = new Suite();
Suite.ReadXml(group, rootSuite);
}
}
public class Suite
{
public List<Suite> suites { get; set; }
public List<Case> cases { get; set; }
public Dictionary<string, string> properties { get; set; }
public string type { get; set; }
public static void ReadXml(XElement xparentSuite, Suite parentSuite)
{
foreach (XElement xSuite in xparentSuite.Elements("suite"))
{
parentSuite.type = (string)xSuite.Attribute("type");
if (parentSuite.suites == null) parentSuite.suites = new List<Suite>();
Suite newSuite = new Suite();
parentSuite.suites.Add(newSuite);
XElement properties = xSuite.Element("properties");
if (properties != null)
{
parentSuite.properties = properties.Elements("property")
.GroupBy(x => (string)x.Attribute("name"), y => (string)y)
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
}
parentSuite.cases = xSuite.Elements("case").Select(x => new Case(x)).ToList();
ReadXml(xSuite, newSuite);
}
}
}
public class Case
{
public string id { get; set; }
public string name { get; set; }
public Dictionary<string, string> properties { get; set; }
public Case() { }
public Case(XElement _case)
{
id = (string)_case.Attribute("id");
name = (string)_case.Attribute("name");
properties = _case.Descendants("property")
.GroupBy(x => (string)x.Attribute("name"), y => (string)y)
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
}
}
}

Related

Search and Update XML using multiple condition using linq

<Students>
<Student Name="John">
<Subject SubId="123" Content="History minor subject" Enrolled="true" Percentage="0"/>
<Subject SubId="146" Content="Math major Subject" Enrolled="true" Percentage="0"/>
</Student>
<Student Name="Jim">
<Subject SubId="564" Content="physics medium subject" Enrolled="true" Percentage="0"/>
<Subject SubId="324" Content="Chem small subject" Enrolled="true" Percentage="0"/>
</Student>
<Students>
Problem 1 - I want to search if the person name content John and content has "major" -- > return the Xelememt , I am trying with below query but it is not working for me.
String studentToSearch = "John" and string contentToSearch = "major"
IEnumerable<XElement> student = from st in rootElement.Descendants("Students").Elements("Student")
where st.Attribute("Name").Value.ToString() == studentToSearch && st.Element("Subject").Attribute("Content").Value.ToString().Contains(contentToSearch)
select st;
Problem 2 - I want to search if the person name content John and content has "major", if the percentage for that find it "0"-- > then update it to "80" percentage. Trying with below query
rootElement.Elements("Students")
.Where(x=> x.Element("Student").Value == studentToSearch)
.Where(a => a.Element("Subject").Attributes("Content").ToString().Contains(contentToSearch) && a.Element("Subject").Attribute("Percentage").Value == "0").FirstOrDefault()
.SetAttributeValue("Percentage", 80);
Any suggestion will be helpful ?
Why not just deserialize the xml? You need to use SelectMany.
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(Students));
Students students = (Students)serializer.Deserialize(reader);
var johnMajor = students.Student.SelectMany(x => x.Subject.Where(y => y.Content.Contains("major")).Select(z => new { name = x.Name, Subject = z })).FirstOrDefault();
if (johnMajor.Subject.Percentage == 0) johnMajor.Subject.Percentage = 80;
}
}
public class Students
{
[XmlElement()]
public List<Student> Student { get; set; }
}
public class Student
{
[XmlAttribute()]
public string Name { get; set; }
[XmlElement]
public List<Subject> Subject { get; set; }
}
public class Subject
{
[XmlAttribute()]
public int SubId { get; set; }
[XmlAttribute()]
public string Content { get; set; }
[XmlAttribute()]
public Boolean Enrolled { get; set; }
[XmlAttribute()]
public int Percentage { get; set; }
}
}

LINQ to XML: How to get element value in C#?

I have this XML structure:
<?xml version="1.0" encoding="utf-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope">
<SOAP-ENV:Header>
<dlv:delivery xmlns:dlv="http://schemas.biztalk.org/btf-2-0/delivery">
<dlv:message>
<dlv:messageID>SDID20200921053955</dlv:messageID>
<dlv:sent>2020-09-21T05:39:55</dlv:sent>
</dlv:message>
<dlv:to>
<dlv:address>urn:schindler:SchindlerDraw:prod</dlv:address>
</dlv:to>
<dlv:from>
<dlv:address>urn:schindler:logical-system:CRM</dlv:address>
</dlv:from>
<dlv:from>
<dlv:system>PC1</dlv:system>
</dlv:from>
</dlv:delivery>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
<asx:values>
<SALESORDER>
<EXTENSIONIN>
<item>
<CONFIRMATIONPRINTDATE />
<CUSTOMEROFFERNOTE />
<CUSTOMERREFERENCE />
</item>
</EXTENSIONIN>
</SALESORDER>
</asx:values>
</asx:abap>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Does anyone know how I can get all the values from the element "item"?
The result should be:
CONFIRMATIONPRINTDATE
CUSTOMEROFFERNOT
CUSTOMERREFERENCE
Thank you in advance.
Use xml linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
XElement delivery = doc.Descendants().Where(x => x.Name.LocalName == "delivery").FirstOrDefault();
XNamespace dlv = delivery.GetNamespaceOfPrefix("dlv");
Envelope envelope = new Envelope();
envelope.messageId = (string)delivery.Descendants(dlv + "messageID").FirstOrDefault();
envelope.sent = (DateTime)delivery.Descendants(dlv + "sent").FirstOrDefault();
envelope.toAddr = (string)delivery.Descendants(dlv + "from").FirstOrDefault().Element(dlv + "address");
envelope.fromAddr = (string)delivery.Descendants(dlv + "to").FirstOrDefault().Element(dlv + "address");
envelope.system = (string)delivery.Descendants(dlv + "system").FirstOrDefault();
envelope.items = doc.Descendants("item").FirstOrDefault().Elements()
.GroupBy(x => x.Name.LocalName, y => (string)y)
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
}
}
public class Envelope
{
public string messageId { get; set; }
public DateTime sent { get; set; }
public string toAddr { get; set; }
public string fromAddr { get; set; }
public string system { get; set; }
public Dictionary<string,string> items { get; set; }
}
}

How to read data from soap service c#

I am creating a .net core web api method to get the data from xml file based on request. The data is from soap service. Below is request Parameter. So i am searching based on the request parameter.(producer code)
Request------>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:get="http://xyz/business/profile_management_ebe4x1">
<soapenv:Header/>
<soapenv:Body>
<get:GetProducerInfo>
<!--Optional:-->
<GetProducerInfoRequest>
<ProducerCode>IT9559</ProducerCode>
</GetProducerInfoRequest>
</get:GetProducerInfo>
</soapenv:Body>
</soapenv:Envelope>
Below is the response i am looking
Response------->
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<dlwmin:GetProducerInfoResponse xmlns:dlwmin="http://xyz/business/Profile_management_ebe4x1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<GetProducerInfoReply xmlns:ns2="http://xyz/get_access/producer_info" xmlns:ns3="http://yzy/business/profile_reply_ebe4x1">
<Producer>
<ProducerName>MARSH & ABC company</ProducerName>
<ResidentState>TEXAS</ResidentState>
<ResidentCity>MIDLAND</ResidentCity>
<ProducerStatus>Open</ProducerStatus>
<ProducerCode>IT6372</ProducerCode>
<MasterCode>272495</MasterCode>
<NationalCode>174144</NationalCode>
<ProducingBranchCode>ABO</ProducingBranchCode>
<CategoryCode>INT</CategoryCode>
</Producer>
<Producer>
<ProducerName>MARSH & ABC company </ProducerName>
<ResidentState>MICHIGAN</ResidentState>
<ResidentCity>LIVONIA</ResidentCity>
<ProducerStatus>Open</ProducerStatus>
<ProducerCode>IT9559</ProducerCode>
<MasterCode>IT9559</MasterCode>
<NationalCode>174144</NationalCode>
<LegacyCode>0036604-99999</LegacyCode>
<ProducingBranchCode>MBO</ProducingBranchCode>
<CategoryCode>GEN</CategoryCode>
</Producer>
I have created xml file in .net solution
XDocument xdoc = XDocument.Load(Path.Combine(Directory.GetCurrentDirectory(), "ProducerResponse.xml"));
XNamespace ns = "http://xyz/get_access/producer_info";
how i can start read from producer node on request.<producer> always does not not contain equal data. some time it contains 9 or 10. data as it looking from the above data.
TIA
Try xml linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
//put response string here
string response = File.ReadAllText(FILENAME);
XDocument doc = XDocument.Parse(response);
List<Producer> producers = doc.Descendants("Producer").Select(x => new Producer()
{
ProducerName = (string)x.Element("ProducerName"),
ResidentState = (string)x.Element("ResidentState"),
ResidentCity = (string)x.Element("ResidentCity"),
ProducerStatus = (string)x.Element("ProducerStatus"),
ProducerCode = (string)x.Element("ProducerCode"),
MasterCode = (string)x.Element("MasterCode"),
NationalCode = (string)x.Element("NationalCode"),
LegacyCode = (string)x.Element("LegacyCode"),
ProducingBranchCode = (string)x.Element("ProducingBranchCode"),
CategoryCode = (string)x.Element("CategoryCode")
}).ToList();
Dictionary<string, Producer> dict1 = producers
.GroupBy(x => x.ProducerCode, y => y)
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
Dictionary<string, List<Producer>> dict2 = producers
.GroupBy(x => x.ProducerCode, y => y)
.ToDictionary(x => x.Key, y => y.ToList());
}
}
public class Producer
{
public string ProducerName { get; set; }
public string ResidentState { get; set; }
public string ResidentCity { get; set; }
public string ProducerStatus { get; set; }
public string ProducerCode { get; set; }
public string MasterCode { get; set; }
public string NationalCode { get; set; }
public string LegacyCode { get; set; }
public string ProducingBranchCode { get; set; }
public string CategoryCode { get; set; }
}
}

c# - how to deserialize only part of xml file

I have a xml:
<query xmlns='http://jabber.org/protocol/disco#info'>
<identity category='store'
type='file'
name='HTTP File Upload' />
<feature var='urn:xmpp:http:upload:0' />
<x type='result' xmlns='jabber:x:data'>
<field var='FORM_TYPE' type='hidden'>
<value>urn:xmpp:http:upload:0</value>
</field>
<field var='max-file-size'>
<value>5242880</value>
</field>
</x>
</query>
I need to get data only from <value> tags. In a result I'd like to get List<string>.
How to do this using System.Xml.Serialization?
Thanks for help
Please do your own thorough research before posting a question. Here are a few methods, your XML has namespaces(Xmlns) too so you need to consider that, go to the source credit for more examples.
- Method XML Namespace (you will most likely need this method)
https://techoctave.com/c7/posts/113-c-reading-xml-with-namespace
- Method XMLnode
`
XmlDocument doc = new XmlDocument();
doc.Load(Path);
XmlNodeList elemList = doc.GetElementsByTagName(...);
for (int i = 0; i < elemList.Count; i++)
{
string attrVal = elemList[i].Attributes["SuperString"].Value;
}
`
- Method Serialization
`
using System;
using System.IO;
using System.Text;
using System.Xml.Serialization;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
var title = new Title() { Id = 3, Value = "something" };
var serializer = new XmlSerializer(typeof(Title));
var stream = new MemoryStream();
serializer.Serialize(stream, title);
stream.Flush();
Console.Write(new string(Encoding.UTF8.GetChars(stream.GetBuffer())));
Console.ReadLine();
}
}
public class Title
{
[XmlAttribute("id")]
public int Id { get; set; }
[XmlText]
public string Value { get; set; }
}
}
`
Source Credit:
Serialize a C# class to XML with attributes and a single value for the class
with-attributes-and-a-single-value-for-the-clas
Read XML Attribute using XmlDocument
xmldocument
Thanks Deleted for your response but I relized that I need data from <field> and <value>
<field var='FORM_TYPE' type='hidden'>
<value>urn:xmpp:http:upload:0</value>
Fortunetly I figured it out by myself:
[XmlType("field")]
public class Field
{
[XmlAttribute("var")]
public string Var { get; set; }
[XmlElement("value")]
public string Value { get; set; }
}
[XmlType("query")]
public class DiscoFileInfo
{
[XmlArray("x",Namespace = "jabber:x:data")]
[XmlArrayItem("field", typeof(Field))]
public List<Field> Fields { get; set; }
}
XmlSerializer xmlSerializer = new XmlSerializer(typeof(DiscoFileInfo), "http://jabber.org/protocol/disco#info");

Convert embedded XML to List<T> within a Repository

I have a situation where in I have to read my master data from XML files.
Hence I have created a Repository which looks like this.
public class XmlReadRepository<T> : IReadRepository<T> where T : class, new()
{
private IEnumerable<T> _data { get; set; }
private IQueryable<T> _query { get; set; }
public XmlReadRepository()
{
Type t = typeof(T);
//Load _data from xml
}
public IQueryable<T> All()
{
return _query;
}
public IQueryable<T> Get(System.Linq.Expressions.Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, string includeProperties = "")
{
throw new NotImplementedException();
}
}
I have exported all my SQL database data into XML file using below SQL query
SELECT *
FROM Company
FOR XML RAW, ROOT('Company')
Whose outputs comes as below
<Company>
<row Id="1" Name="ABC" StartDate="2000-03-01" />
<row Id="2" Name="XYZ" StartDate="2001-13-11" />
</Company>
The xml is physically saved to a file and is embedded as a resource.
Now I am looking for some fast way to serialize the xml data into list.
I have tried loading the xml file with stream and then using reflection created objects by iterating through the XML. Is there any better way by which I can serialize the xml data to my C# object.
Also please share your inputs on whether is it good to have xml as an embedded resource as the size of each xml can be around 8MB and there are 5 such XMLs.
You can turn my code into a function
public class Row
{
private static XmlTextReader reader = null;
public int Id { get; set; }
public string name { get; set; }
public DateTime startDate { get; set; }
public Row() { }
public Row(TextReader sReader)
{
reader = new XmlTextReader(sReader); //can be filename instead of stringreader
}
public Row Get(string elementName)
{
Row results = null;
if (!reader.EOF)
{
if (reader.Name != elementName)
{
reader.ReadToFollowing(elementName);
}
if (!reader.EOF)
{
XElement newRow = (XElement)XElement.ReadFrom(reader);
results = new Row() { Id = (int)newRow.Attribute("Id"), name = (string)newRow.Attribute("Name"), startDate = (DateTime)newRow.Attribute("StartDate") };
}
}
return results;
}
}​
Use xml linq like below
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string xml =
"<Company>" +
"<row Id=\"1\" Name=\"ABC\" StartDate=\"2000-03-01\" />" +
"<row Id=\"2\" Name=\"XYZ\" StartDate=\"2001-13-11\" />" +
"</Company>";
XDocument doc = XDocument.Parse(xml); // use Load to read from file
var rows = doc.Descendants("row").Select(x => new
{
id = (int)x.Attribute("Id"),
name = (string)x.Attribute("Name"),
startDate = (DateTime)x.Attribute("StartDate")
}).ToList();
//using xmltextreader
List<Row> rows2 = new List<Row>();
StringReader sReader = new StringReader(xml);
XmlTextReader reader = new XmlTextReader(sReader); //can be filename instead of stringreader
while(!reader.EOF)
{
if (reader.Name != "row")
{
reader.ReadToFollowing("row");
}
if (!reader.EOF)
{
XElement newRow = (XElement)XElement.ReadFrom(reader);
rows2.Add(new Row() { Id = (int)newRow.Attribute("Id"), name = (string)newRow.Attribute("Name"), startDate = (DateTime)newRow.Attribute("StartDate") });
}
}
}
}
public class Row
{
public int Id { get; set; }
public string name { get; set; }
public DateTime startDate { get; set; }
}
}
​

Categories

Resources