XMl deserializer in C# - c#

I have the following XML, and I only want to deserialize the streams of Product1, what would be the syntax in C#? Thanks. I couldn't find any documentations online.
<ArrayOfProductData>
- <ProductData>
<ProductName>product1</ProductName>
<ProductID>1</ProductID>
- <Streams>
<productId>1</productId>
<name>current stream</name>
<id>1</id>
</Streams>
- <Streams>
<productId>1</productId>
<name>stream 1.1</name>
<id>2</id>
</Streams>
</ProductData>
- <ProductData>
<ProductName>product2</ProductName>
<ProductID>2</ProductID>
- <Streams>
<productId>2</productId>
<name>current stream</name>
<id>1</id>
</Streams>
- <Streams>
<productId>2</productId>
<name>stream 1.2</name>
<id>2</id>
</Streams>
</ProductData>
</ArrayOfProductData>

You could use XDocument and XPath to filter:
using System;
using System.Linq;
using System.Xml.Linq;
using System.Xml.XPath;
public class ProductStream
{
public int Id { get; set; }
public int ProductId { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main()
{
var streams = XDocument
.Load("test.xml")
.XPathSelectElements("//ProductData[ProductID='1']/Streams")
.Select(s => new ProductStream
{
Id = int.Parse(s.Element("id").Value),
ProductId = int.Parse(s.Element("productId").Value),
Name = s.Element("name").Value
});
foreach (var stream in streams)
{
Console.WriteLine(stream.Name);
}
}
}

I will not write your code. Have a look at http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx and write a class which might fit your needs, and serialize it. Get familiar with the attributes used to control serialization and tune your class to be serialized the way your example looks like. Then your are able to use it for deserialization too.
Of course there are other options to read the data from the XML, but I'll not document all of them. Obviously you could also read the data "by hand" using , XmlDocument, XDocument, XmlReader, ... or whatever fits your requirements.

Did you checked this? XmlSerializer.Deserialize Method (Stream) on MSDN

You can't really do selective deserialization, but you can filter the results after it's been deserialized to something like an XDocument object. EG:
using System.Xml.Linq;
XDocument myDoc = XDocument.Load("myfile.xml");
var prod1Streams = from e in XDocument.Root.Elements("Streams")
where e.Element("productId") != null
&& e.Element("productId").Value == "1"
select e;

Related

Iteration on XML tags in C#

How would code an iteration to loop through the parent tag on a xml file as below:
<collection>
<parent>
<child1>DHL</child1>
<child2>9000000131</child2>
<child3>ISS Gjøvik</child13>
<child4>ISS Gjøvik</child4>
<child5>ISS Gjøvik</child5>
<child6>9999000000136</child6>
</parent>
<parent>
<child1>DHL</child1>
<child2>9000000132</child2>
<child3>ISS Gjøvik</child13>
<child4>ISS Gjøvik</child4>
<child5>ISS Gjøvik</child5>
<child6>9999000000136</child6>
</parent>
<parent>
<child1>DHL</child1>
<child2>9000000134</child2>
<child3>ISS Gjøvik</child13>
<child4>ISS Gjøvik</child4>
<child5>ISS Gjøvik</child5>
<child6>9999000000136</child6>
</parent>
</collection>
I need to insert the value of child1 as the primary key into the DB.
Have you tried the XmlReader? What do you have so far? Please show us some code. Just a reminder, StackOverflow is a helpdesk, not a programming service.
I see DHL in one of the tags. If that refers to the postal delivery company, they have an API (SDK) that is easy to use from within .NET code..
If you want to use XML (de)serialization that I would suggest that you start reading the System.Xml.Serialization namespace documentation. Microsoft has provided more than enough documentation and examples.
Link to namespace docs: https://msdn.microsoft.com/en-us/library/system.xml.serialization(v=vs.110).aspx
Here are some examples that contains anything that you would need to deserialzation the xml document to a poco class:
https://msdn.microsoft.com/en-us/library/58a18dwa(v=vs.110).aspx
Assuming your xml is in the string variable xml:
var xdoc = XDocument.Parse(xml);
foreach (var parentEls in xdoc.Root.Elements("parent"))
{
string child1Value = parentEls.Element("child1").Value;
// your logic using child1 value
}
Note that your xml is malformed - <child3> is closed by </child13>.
Using xml linq to parse everything
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);
var results = doc.Descendants("parent").Select(x => new {
child1 = (string)x.Element("child1"),
child2 = (string)x.Element("child2"),
child3 = (string)x.Element("child3"),
child4 = (string)x.Element("child4"),
child5 = (string)x.Element("child5"),
child6 = (string)x.Element("child6")
}).ToList();
}
}
}

Filter XML with dynamic where clause

I have the following XML:
<Events>
<Event>
<EventID displayName="Event ID">1</EventID>
<EventName displayName="Event Name">Some event</EventName>
<OrgID displayName="Organization ID">8</OrgID>
</Event>
<Event>
<EventID displayName="Event ID">2</EventID>
<EventName displayName="Event Name">Another Event</EventName>
<OrgID displayName="Organization ID">10</OrgID>
</Event>
</Events>
I want to be able to filter them by a where clause constructed dynamically. For example:
Where("Event ID = 2 AND (Organization ID = 8 OR Organization ID = 10)")
Please note that I can only use the displayName to filter the data. Obviously I can get the tag name from the displayName and construct the where clause, but that means some added calculations on the clients machine, which if possible, I'd like to avoid.
I have explored the options to use Dynamic Linq or DataTable.Select() but with my limited knowledge on Linq, I cant seem to find an easy way to adopt those to filter XML data. Any help/hint is appreciated.
Using LINQ-to-XML dot notation to extract the elements:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
string content = #"<Events>
<Event>
<EventID displayName=""Event ID"">1</EventID>
<EventName displayName=""Event Name"">Some event</EventName>
<OrgID displayName=""Organization ID"">8</OrgID>
</Event>
<Event>
<EventID displayName=""Event ID"">2</EventID>
<EventName displayName=""Event Name"">Another Event</EventName>
<OrgID displayName=""Organization ID"">10</OrgID>
</Event>
</Events>";
XDocument doc = XDocument.Load(new System.IO.StringReader(content));
var events = doc.Descendants("Event")
.Where(p => p.Elements("EventID").First().Value == "1")
.Where(p => p.Elements("OrgID").First().Value == "8" || p.Elements("OrgID").First().Value == "10");
}
}
}
The System.XML.Linq namespace is incredibly useful.
Parse the xml completely
var eventList=doc.Elements("Event")
.Select(x=>new
{
EventId=int.Parse(x.Element("EventId").Value),
EventName=x.Element("EventName").Value,
OrgID=int.Parse(x.Element("OrgID").Value)
});
You could now make a generalized method,
public string getEventName(int eventId,params int[] orgId)
{
return eventList.Where(x=>
x.EventId==eventId &&
orgId.Any(y=>y==x.OrgID))
.Select(x.EventName)
.Single();
}
Now you can do
getEventName(2);
getEventName(2,8);
getEventName(2,8,10);
So want you want to do is to use linq syntax to get data from your xml ?
Something like that?
XDocument loadedXML = XDocument.Load('Your xml file name');
var yourStrongTypeEventsList
= (from event in loadedXML.Descendants("Event")
where event.Element("EventID").Attribute("displayName").Value.Equals("Your value here")
select new
{
EventID = event.Element("EventID").Value,
EventName = event.Element("EventName").Value
}).ToList();

How can I format the XML response of my REST web service?

I am playing around with this simple tutorial in C# and here is the kind of XML you can GET.
<Person xmlns="http://schemas.datacontract.org/2004/07/RESTfulDemo"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Age>23</Age>
<ID>1</ID>
<Name>Bob Kohler</Name>
</Person>
Here is the Person.cs class :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Runtime.Serialization;
namespace RESTfulDemo
{
[DataContract]
public class Person
{
[DataMember]
public string ID;
[DataMember]
public string Name;
[DataMember]
public string Age;
}
}
1) How should can I add attributes / Prefixes in my XML for each data member ?
2) How can I set the heading of my XML to this (or anything else) :
<?xml version="1.0"?>
question 2) can be done when initializing a document:
XDocument document = new XDocument(new XDeclaration("1.0", "utf-16", "yes"));
as for question 1), from what I have understood, if you have such XML file:
<CATALOG>
<CD>
<TITLE> ... </TITLE>
<ARTIST> ... </ARTIST>
<YEAR> ... </YEAR>
</CD>
</CATALOG>
and you need to add an attribute "id" for the CD node,
(where the id is automatically incremented)
XmlNodeList list = document.GetElementsByTagName("CATALOG");
int i = 0;
foreach (XmlNode CD in list)
{
i++;
XmlAttribute idAttr = document.CreateAttribute("id");
idAttr.Value = i.ToString();
CD.Attributes.Append(idAttr); //to append the created attribute and its value to the CD node
}
So, here is what I did to overcome this problem. The solution does not involve Serialization but at least it enables to format the response like you want.
(1) Put XElement from System.xml.linq as return type of each method and build the xml with the XElement class in each method.
(2) Use exactly the code provided here to add the xml declaration on top of your xml response. Thanks to #Dash for the link.

C# object to XML

I am creating an application which requires to convert c# object to XML.
I am using XML Serializer class to achieve this. Here is the code snippet:
public class Anwer
{
public int ID { get; set; }
public string XML { get; set; }
public Anwer(int ID, string XML)
{
this.ID = ID;
this.XML = XML;
}
public Anwer() { }
}
Here is the main function:
string AnswerXML = #"<Answer>1<Answer>";
List<Anwer> answerList = new List<Anwer>();
answerList.Add(new Anwer(1,AnswerXML));
AnswerXML = #"<Answer>2<Answer>";
answerList.Add(new Anwer(2, AnswerXML));
XmlSerializer x = new XmlSerializer(answerList.GetType());
x.Serialize(Console.Out, answerList);
The output is:
<?xml version="1.0" encoding="IBM437"?>
<ArrayOfAnwer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="h
ttp://www.w3.org/2001/XMLSchema">
<Anwer>
<ID>1</ID>
<XML><Answer>1<Answer></XML>
</Anwer>
<Anwer>
<ID>2</ID>
<XML><Answer>2<Answer></XML>
</Anwer>
</ArrayOfAnwer>
In the above code '<' and '>' are getting replaced by '<' and '&gt';
How to avoid this?
I know string replace is one of the way, but I don't want to use it.
Thanks in advance.
You don't, basically. That's correctly serializing the object - the XML serializer doesn't want to have to deal with XML within strings messing things up, so it escapes the XML appropriately.
If you deserialize the XML later, you'll get back to the original object data.
If you're trying to build up an XML document in a custom fashion, I suggest you don't use XML serialization to start with. Either use LINQ to XML if you're happy to create elements etc explicitly, or if you really, really want to include arbitrary strings directly in your output, use XmlWriter.
If you could give us more information about the bigger picture of what you're trying to do, we may be able to suggest better alternatives - building XML strings directly is almost never a good idea.
XmlSerializer won't believe you that an element is xml unless you convince it, for example by exposing that property as an XmlDocument. Otherwise, it (correctly, IMO) always encodes such values. For example:
using System;
using System.Xml;
using System.Xml.Serialization;
public class Anwer
{
public int ID { get; set; }
public XmlDocument XML { get; set; }
public Anwer(int ID, string XML)
{
this.ID = ID;
XmlDocument doc = new XmlDocument();
doc.LoadXml(XML);
this.XML = doc;
}
public Anwer()
{ }
}
static class Program
{
static void Main()
{
var answer = new Anwer(123, "<Answer>2</Answer>");
var ser = new XmlSerializer(answer.GetType());
ser.Serialize(Console.Out, answer);
}
}
I am creating an application which requires to convert c# object to XML. I am using XML Serializer class to achieve this
If you're using the XML Serializer to do the work, then why the "XML" field where you're inserting hand-coded XML? Seems like you want something more like this (using your class name, though it looks like a misspelling):
public class Anwer
{
public int ID { get; set; }
public int Answer { get; set; }
}
..
List<Anwer> answerList = new List<Anwer>() {
new Anwer { ID=1, Answer=2 },
new Anwer { ID=2, Answer=3 },
};
XmlSerializer x = new XmlSerializer(answerList.GetType());
x.Serialize(Console.Out, answerList);
..
<ArrayOfAnwer ...>
<Anwer>
<ID>1</ID>
<Answer>2</Answer>
</Anwer>
...
Or if you actually want/need the Answer element to be nested in an XML element for some reason, you can alter your Anwer object to reflect that structure (as Oleg Kalenchuk suggests), or generate the XML yourself rather than using the serializer:
XElement xml = new XElement("AnwerList",
from anwer in anwerList select
new XElement("Anwer",
new XElement("ID", anwer.ID),
new XElement("XML",
new XElement("Answer", anwer.Answer)
)
)
);
Console.Out.WriteLine(xml);
<AnwerList>
<Anwer>
<ID>1</ID>
<XML>
<Answer>2</Answer>
</XML>
</Anwer>
...
I prefer the latter anyway, because it gives you more control.
You're assigning a string containing the < and > sign to the XML element so it is obvious that teh serializer would replace the < and > with entity references. Even if you're getting > in the text when you deserialise the XML you'll get the > in your text.
Create a new class AnswerXML with one integer "Answer" member
Change type of XML member to AnswerXML instead of string
Because '<' and '>' are characters used for the xml-structure itself, they are automatically htmlencoded. When you read it back in your app and deserialize it, the '<' and '>' should be converted back to '<' and '>'.
If your goal is otherwise, use htmldecode functionality.
If this don't help, just tell what exactly you want to do with the xml-data.

Query an XDocument for elements by name at any depth

I have an XDocument object. I want to query for elements with a particular name at any depth using LINQ.
When I use Descendants("element_name"), I only get elements that are direct children of the current level. I'm looking for the equivalent of "//element_name" in XPath...should I just use XPath, or is there a way to do it using LINQ methods?
Descendants should work absolutely fine. Here's an example:
using System;
using System.Xml.Linq;
class Test
{
static void Main()
{
string xml = #"
<root>
<child id='1'/>
<child id='2'>
<grandchild id='3' />
<grandchild id='4' />
</child>
</root>";
XDocument doc = XDocument.Parse(xml);
foreach (XElement element in doc.Descendants("grandchild"))
{
Console.WriteLine(element);
}
}
}
Results:
<grandchild id="3" />
<grandchild id="4" />
An example indicating the namespace:
String TheDocumentContent =
#"
<TheNamespace:root xmlns:TheNamespace = 'http://www.w3.org/2001/XMLSchema' >
<TheNamespace:GrandParent>
<TheNamespace:Parent>
<TheNamespace:Child theName = 'Fred' />
<TheNamespace:Child theName = 'Gabi' />
<TheNamespace:Child theName = 'George'/>
<TheNamespace:Child theName = 'Grace' />
<TheNamespace:Child theName = 'Sam' />
</TheNamespace:Parent>
</TheNamespace:GrandParent>
</TheNamespace:root>
";
XDocument TheDocument = XDocument.Parse( TheDocumentContent );
//Example 1:
var TheElements1 =
from
AnyElement
in
TheDocument.Descendants( "{http://www.w3.org/2001/XMLSchema}Child" )
select
AnyElement;
ResultsTxt.AppendText( TheElements1.Count().ToString() );
//Example 2:
var TheElements2 =
from
AnyElement
in
TheDocument.Descendants( "{http://www.w3.org/2001/XMLSchema}Child" )
where
AnyElement.Attribute( "theName" ).Value.StartsWith( "G" )
select
AnyElement;
foreach ( XElement CurrentElement in TheElements2 )
{
ResultsTxt.AppendText( "\r\n" + CurrentElement.Attribute( "theName" ).Value );
}
You can do it this way:
xml.Descendants().Where(p => p.Name.LocalName == "Name of the node to find")
where xml is a XDocument.
Be aware that the property Name returns an object that has a LocalName and a Namespace. That's why you have to use Name.LocalName if you want to compare by name.
Descendants will do exactly what you need, but be sure that you have included a namespace name together with element's name. If you omit it, you will probably get an empty list.
There are two ways to accomplish this,
LINQ to XML
XPath
The following are samples of using these approaches,
List<XElement> result = doc.Root.Element("emails").Elements("emailAddress").ToList();
If you use XPath, you need to do some manipulation with the IEnumerable:
IEnumerable<XElement> mails = ((IEnumerable)doc.XPathEvaluate("/emails/emailAddress")).Cast<XElement>();
Note that
var res = doc.XPathEvaluate("/emails/emailAddress");
results either a null pointer, or no results.
I am using XPathSelectElements extension method which works in the same way to XmlDocument.SelectNodes method:
using System;
using System.Xml.Linq;
using System.Xml.XPath; // for XPathSelectElements
namespace testconsoleApp
{
class Program
{
static void Main(string[] args)
{
XDocument xdoc = XDocument.Parse(
#"<root>
<child>
<name>john</name>
</child>
<child>
<name>fred</name>
</child>
<child>
<name>mark</name>
</child>
</root>");
foreach (var childElem in xdoc.XPathSelectElements("//child"))
{
string childName = childElem.Element("name").Value;
Console.WriteLine(childName);
}
}
}
}
Following #Francisco Goldenstein answer, I wrote an extension method
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Mediatel.Framework
{
public static class XDocumentHelper
{
public static IEnumerable<XElement> DescendantElements(this XDocument xDocument, string nodeName)
{
return xDocument.Descendants().Where(p => p.Name.LocalName == nodeName);
}
}
}
This my variant of the solution based on LINQ and the Descendants method of the XDocument class
using System;
using System.Linq;
using System.Xml.Linq;
class Test
{
static void Main()
{
XDocument xml = XDocument.Parse(#"
<root>
<child id='1'/>
<child id='2'>
<subChild id='3'>
<extChild id='5' />
<extChild id='6' />
</subChild>
<subChild id='4'>
<extChild id='7' />
</subChild>
</child>
</root>");
xml.Descendants().Where(p => p.Name.LocalName == "extChild")
.ToList()
.ForEach(e => Console.WriteLine(e));
Console.ReadLine();
}
}
Results:
For more details on the Desendants method take a look here.
We know the above is true. Jon is never wrong; real life wishes can go a little further.
<ota:OTA_AirAvailRQ
xmlns:ota="http://www.opentravel.org/OTA/2003/05" EchoToken="740" Target=" Test" TimeStamp="2012-07-19T14:42:55.198Z" Version="1.1">
<ota:OriginDestinationInformation>
<ota:DepartureDateTime>2012-07-20T00:00:00Z</ota:DepartureDateTime>
</ota:OriginDestinationInformation>
</ota:OTA_AirAvailRQ>
For example, usually the problem is, how can we get EchoToken in the above XML document? Or how to blur the element with the name attribute.
You can find them by accessing with the namespace and the name like below
doc.Descendants().Where(p => p.Name.LocalName == "OTA_AirAvailRQ").Attributes("EchoToken").FirstOrDefault().Value
You can find it by the attribute content value, like this one.
(Code and Instructions is for C# and may need to be slightly altered for other languages)
This example works perfect if you want to read from a Parent Node that has many children, for example look at the following XML;
<?xml version="1.0" encoding="UTF-8"?>
<emails>
<emailAddress>jdoe#set.ca</emailAddress>
<emailAddress>jsmith#hit.ca</emailAddress>
<emailAddress>rgreen#set_ig.ca</emailAddress>
</emails>
Now with this code below (keeping in mind that the XML File is stored in resources (See the links at end of snippet for help on resources) You can obtain each email address within the "emails" tag.
XDocument doc = XDocument.Parse(Properties.Resources.EmailAddresses);
var emailAddresses = (from emails in doc.Descendants("emailAddress")
select emails.Value);
foreach (var email in emailAddresses)
{
//Comment out if using WPF or Windows Form project
Console.WriteLine(email.ToString());
//Remove comment if using WPF or Windows Form project
//MessageBox.Show(email.ToString());
}
Results
jdoe#set.ca
jsmith#hit.ca
rgreen#set_ig.ca
Note: For Console Application and WPF or Windows Forms you must add the "using System.Xml.Linq;" Using directive at the top of your project, for Console you will also need to add a reference to this namespace before adding the Using directive. Also for Console there will be no Resource file by default under the "Properties folder" so you have to manually add the Resource file. The MSDN articles below, explain this in detail.
Adding and Editing Resources
How to: Add or Remove Resources

Categories