c# - how to deserialize only part of xml file - c#

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

Related

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

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

Creating a XML file from a database with a model C#

So I need to create a method that makes an XML file from a database, I have already written the stored procedures that get the information from a database for the XML now I only need to write the part where I convert my database to a XML file using the properties of another class that I have written as nodes.
public string CreateXML(Object YourClassObject){
XmlDocument xmlDoc =new XmlDocument(); //Represents an XML document,
// Initializes a new instance of the XmlDocument class.
XmlSerializer xmlSerializer = new XmlSerializer(YourClassObject.GetType());
// Creates a stream whose backing store is memory.
using (MemoryStream xmlStream =new MemoryStream())
{
xmlSerializer.Serialize(xmlStream, YourClassObject);
xmlStream.Position = 0;
//Loads the XML document from the specified string.
xmlDoc.Load(xmlStream);
return xmlDoc.InnerXml;
}
}
This is some code I found online I think I can use for serializing my model, but i'm accessing the database over an event that I created (I'll provide the code tomorrow when I get to work). Anyway I'm accesing the database in the event like the following e.DataTable. Any ideas anybody?
My model looks like the following:
public class DataModel
{
string Sifra {get; set;}
public string Naziv {get; set;}
public string JM {get; set;}
public int Kolicina {get; set;}
public float Cena_x0020_vp {get; set;}
public float Cena_x0020_mp {get; set;}
public float Cena_x0020_bod {get; set;}
public string Slika {get; set;}
public string Grupa {get; set;}
}
This is a sample of how my generated XML should look like.
<?xml version="1.0" encoding="UTF-8"?>
<dataroot xmlns:od="urn:schemas-microsoft-com:officedata" generated="2019-04-17T19:13:54">
<row>
<Sifra>ADA110-100</Sifra>
<Naziv_x0020_artikla>Adapter 220/110VAC 100W</Naziv_x0020_artikla>
<JM>kom</JM>
<Kolicina>1</Kolicina>
<Cena_x0020_vp>2683.33</Cena_x0020_vp>
<Cena_x0020_mp>3220</Cena_x0020_mp>
<Cena_x0020_bod>28</Cena_x0020_bod>
<Slika1> ada110v.jpg</Slika1>
<Grupa>Adateri 110V AC</Grupa>
</row>
Problem solved:
private void CreateXML(DataTable dataTable)
{
var list = new List<Row>();
XmlSerializer writer = new XmlSerializer(typeof(List<Row>));
var path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\ExportZaWeb.xml";
FileStream file = File.Create(path);
foreach (DataRow row in dataTable.Rows)
{
Row r = new Row();
r.Naziv = row["Naziv Artikla"].ToString();
r.JM = row["JM"].ToString();
r.Kolicina = row["Kolicina"].ToString();
r.Cena_x0020_vp = row["Cena vp"].ToString();
r.Cena_x0020_mp = row["Cena mp"].ToString();
r.Cena_x0020_bod = row["Cena bod"].ToString();
r.Slika = row["Slika1"].ToString();
r.Grupa = row["Grupa"].ToString();
list.Add(r);
}
writer.Serialize(file, list);
file.Close();
}
Why not let the stored procedure return the xml for you. The query in the stored procedure would lool something like this:
SELECT Sifra, Naziv, JM, Kolicina, Cena_x0020_vp, Cena_x0020_mp, Cena_x0020_bod, Slika, Grupa
FROM DataModel
FOR XML AUTO
Create a method which takes in a list or IEnumerable object of the Models and returns a string containing the XML (untested, but should get you started), this is assuming you already have the database data in usable objects:
public string GetXmlForModels(IEnumerable<DataModel> dataModels)
{
//Assume a list of DataModels is in dataModels of type IEnumerable<DataModel>
var doc = new XmlDocument();
foreach (var model in dataModels)
{
var row = (XmlElement)doc.AppendChild(doc.CreateElement("row"));
row.SetAttribute("xmlns:od", "urn:schemas-microsoft-com:officedat");
row.SetAttribute("generated", DateTime.Now.ToString("yy-MM-ddTHH:mm:ss"));
var sifraElement = doc.CreateElement("Sifra");
sifraElement.InnerText = model.Sifra;
row.AppendChild(sifraElement);
//Repeat top 3 lines for each element ...
doc.AppendChild(row);
}
return doc.OuterXml;
}
Use XML Serializer :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication110
{
class Program
{
const string INPUT_FILENAME = #"c:\temp\test.xml";
const string OUTPUT_FILENAME = #"c:\temp\test1.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(INPUT_FILENAME);
string xml = reader.ToString();
XmlSerializer serializer = new XmlSerializer(typeof(DataRoot));
DataRoot root = (DataRoot)serializer.Deserialize(reader);
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
XmlWriter writer = XmlWriter.Create(OUTPUT_FILENAME,settings);
serializer.Serialize(writer, root);
}
}
[XmlRoot(ElementName = "dataroot", Namespace = "")]
public class DataRoot
{
[XmlElement(ElementName = "row", Namespace = "")]
public List<DataModel> rows { get; set; }
}
[XmlRoot(ElementName = "row", Namespace = "")]
public class DataModel
{
string Sifra { get; set; }
public string Naziv { get; set; }
public string JM { get; set; }
public int Kolicina { get; set; }
public float Cena_x0020_vp { get; set; }
public float Cena_x0020_mp { get; set; }
public float Cena_x0020_bod { get; set; }
public string Slika { get; set; }
public string Grupa { get; set; }
}
}

How to parse xml with LINQ to an Object with XDocument

I have an xml file as below:
<Message xsi:schemaLocation ="..">
<Header>..</Header>
<Body>
<History
xmlns="..">
<Number></Number>
<Name></Name>
<Item>
<CreateDate>..</CreateDate>
<Type>..</Type>
<Description></Description>
</Item>
<Item>
<CreateDate>..</CreateDate>
<Type>..</Type>
<Description>1..</Description>
<Description>2..</Description>
</Item>
</History>
</Body>
</Message>
I would like to create and object from this as History object.
public class History
{
public string Name { get; set; }
public string Number { get; set; }
public List<Item> Items { get; set; }
}
var xElement = XDocument.Parse(xmlString);
XElement body = (XElement)xElement.Root.LastNode;
XElement historyElement = (XElement)body.LastNode;
var history = new History
{
Name = (string)historyElement.Element("Name"),
Number = (string)historyElement.Element("Number"),
Items = (
from e in historyElement.Elements("Item")
select new Item
{
CraeteDate = DateTime.Parse(e.Element("CreateDate").Value),
Type = (string)e.Element("Type").Value,
Description = string.Join(",",
from p in e.Elements("Description") select (string)p.Element("Description"))
}).ToList()
};
Why this does not work?
The values are always null.
It seems that "historyElement.Element("Name")" is always null even there is an element and value for the element.
Any idea what am I missing?
Thanks
It's due to the namespace, try doing this:
XNamespace ns = "http://schemas.microsoft.com/search/local/ws/rest/v1";// the namespace you have in the history element
var xElement = XDocument.Parse(xmlString);
var history= xElement.Descendants(ns+"History")
.Select(historyElement=>new History{ Name = (string)historyElement.Element(ns+"Name"),
Number = (string)historyElement.Element(ns+"Number"),
Items = (from e in historyElement.Elements(ns+"Item")
select new Item
{
CraeteDate= DateTime.Parse(e.Element(ns+"CreateDate").Value),
Type = (string) e.Element(ns+"Type").Value,
Description= string.Join(",",
from p in e.Elements(ns+"Description") select (string)p)
}).ToList()
}).FirstOrDefault();
If you want to read more about this subject, take a look this link
A couple of minor things here, the xml was malformed here. So had to take a while to test and make it work.
You have an xsi in the front which I assume should be somewhere mentioned in the xsd.
Turns out you have to append the namespace if your xml node has a namespace attached to it as you are parsing the xml tree here:
My sample solution looked like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace ConsoleApplication1
{
public class History
{
public string Name { get; set; }
public string Number { get; set; }
public List<Item> Items { get; set; }
}
public class Item
{
public DateTime? CreateDate { get; set; }
public string Type { get; set; }
public string Description { get; set; }
}
class Program
{
static void Main(string[] args)
{
string xmlString =
#"<Message>
<Header>..</Header>
<Body>
<History
xmlns=""http://schemas.somewhere.com/types/history"">
<Number>12</Number>
<Name>History Name</Name>
<Item>
<CreateDate></CreateDate>
<Type>Item 1 Type</Type>
<Description>Item 1 Description</Description>
</Item>
<Item>
<CreateDate></CreateDate>
<Type>Item 2 Type</Type>
<Description>Item 2 Description 1</Description>
<Description>Item 2 Description 2</Description>
</Item>
</History>
</Body>
</Message>";
XNamespace ns = "http://schemas.somewhere.com/types/history";
var xElement = XDocument.Parse(xmlString);
var historyObject = xElement.Descendants(ns +"History")
.Select(historyElement => new History
{
Name = historyElement.Element(ns + "Name")?.Value,
Number = historyElement.Element(ns + "Number")?.Value,
Items = historyElement.Elements(ns + "Item").Select(x => new Item()
{
CreateDate = DateTime.Parse(x.Element(ns + "CreateDate")?.Value),
Type = x.Element(ns + "Type")?.Value,
Description = string.Join(",", x.Elements(ns + "Description").Select(elem=>elem.Value))
}).ToList()
}).FirstOrDefault();
}
}
}
If you dont wan't to care about finding the namespace, you might want to try the following:
var document = XDocument.Parse(xmlString);
var historyObject2 = document.Root.Descendants()
.Where(x=>x.Name.LocalName == "History")
.Select(historyElement => new History
{
Name = historyElement.Element(historyElement.Name.Namespace + "Name")?.Value,
Number = historyElement.Element(historyElement.Name.Namespace+ "Number")?.Value,
Items = historyElement.Elements(historyElement.Name.Namespace + "Item").Select(x => new Item()
{
//CreateDate = DateTime.Parse(x.Element("CreateDate")?.Value),
Type = x.Element(historyElement.Name.Namespace + "Type")?.Value,
Description = string.Join(",", x.Elements(historyElement.Name.Namespace + "Description").Select(elem => elem.Value))
}).ToList()
}).FirstOrDefault();

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

Binding XML data to Model in C# MVC

I'm looking to bind XML to Model in C# MVC app.
XML:
<people>
<person>
<name>Mr Brown</name>
<age>40</age>
<hobby>
<title>Eating</title>
<description>eats a lot</description>
</hobby>
<hobby>
<title>Sleeping</title>
<description>sleeps a lot</description>
</hobby>
</person>
<person>
<name>Mr White</name>
<age>40</age>
<hobby>
<title>Skiing</title>
<description>more details on this</description>
</hobby>
<hobby>
<title>Football</title>
<description>watches football</description>
</hobby>
</person>
</people>
Model:
public class People
{
public string Name { get; set; }
public string Age { get; set; }
public IList<Hobbies> Hobby {get; set; }
}
public class Hobbies
{
public string Title { get; set; }
public string Description { get; set; }
}
Broken Binding:
var person = from a in xml.Descendants("person")
select new People
{
Name = a.Element("name").Value,
Age = a.Element("age").Value,
Hobby = *WHAT GOES HERE?*
}
I'n new to C# and looking for the best way to bind the data from the XML to the person var. Which I'll later loop over and output in an HTML table.
Any help would be great.
You have to do it this way:
var person = from a in xml.Descendants("person")
select new People
{
Name = a.Element("name").Value,
Age = a.Element("age").Value,
Hobby = a.Descendants("hobby")
.Select(x=> new Hobbies
{
Title =x.Element("title").Value,
Description = x.Element("description").Value
}).ToList()
};
WORKING FIDDLE:
https://dotnetfiddle.net/2uKdd5
Looks like you want standard XML deserialization. Some good answers on the best way to do that here
I would use XmlSerializer to load from Xml and to save to xml.
You can derive People from this class for example (SerializeManagement) :
public class SerializeManagement<T>
{
public static T ReadFromXML(string iPath)
{
T val = default(T);
try
{
// load from XML
using (var sw = new StreamReader(iPath, Encoding.Default))
{
var ser = new XmlSerializer(typeof(T));
val = (T)ser.Deserialize(sw);
}
}
catch
{
Console.WriteLine("Problem reading from xml data file.");
}
return val;
}
public void SaveToXML(string iPath)
{
try
{
//TODO => using
var sw = new StreamWriter(iPath, false, Encoding.Default);
var ser = new XmlSerializer(typeof(T));
ser.Serialize(sw, this);
sw.Close();
}
catch
{
Console.WriteLine("Problem saving to xml data file.");
}
}
}
If you encounter problems, this could be because of your model definition or xml structure :
Then you can :
1) Generate c# class from the xml using xsd utility;
2) Generate XML from existing class using SaveToXML. That way you are sure the XML structure is compliant with your model.
Enjoy !

Categories

Resources