c# XML and LINQ - c#

I have an XML doc that looks like so:
<people>
<person>
<name>mike</name>
<address>1 main st</address>
<jobTitle>SE</jobTitme>
<children>
<name>mary</name>
<age>5</age>
</childres>
</person>
<person>
<name>john</name>
<address>2 main st</address>
<jobTitle>SE</jobTitme>
</person>
</people>`
So not all of the person blocks and a children block. Pretty simple. When I add a new person to the XML via C#, I am writing a function that takes a person object and that person object has a collection of children objects (which may be 0 or more). I am having trouble writing the linq in that function. I can easily add a person object, but conditionally adding 1 or more children is tough. Here is what I have so far:
doc.Element("People").Add(
new XElement("Person",
new XElement("Name", person.name),
new XElement("Address", person.address),
new XElement("jobTitle", person.jobTitle)))
how can I conditionally add the children if they exist?
public class person
{
public List<Child> childList;
public string name;
public string address;
public string jobTitle
}
public class child
{
public string name;
public int age;
}

how can I conditionally add the children if they exist?
Three options:
Use a null argument in the XElement call; that will be ignored
Pass in an empty sequence of children; again, this will be irrelevant
Build the rest of the element, then just conditionally call Add afterwards.
It's hard to give more concrete advice without seeing the code for your Person type.
(As an aside, it looks like your element should actually be child rather than children, assuming that you have one element per child...)
EDIT: Now that we can see your code, it looks like you just want:
doc.Element("People").Add(
new XElement("Person",
new XElement("Name", person.name),
new XElement("jobTitle", job.title),
person.children.Select(c => new XElement("children",
new XElement("Name", c.name),
new XElement("Age", c.age)))));
Note that you're currently being very inconsistent with your capitalization when it comes to element names, and also it's a bad idea to expose public fields like this.

Related

LINQ to XML parsing single object

I am trying to parse XML document like this:
<root>
<first>1</first>
<second>2</second>
</root>
To structure like this:
class SomeClass
{
...
public string First;
public string Second;
}
but as far as I understood, I can create new object only in select statement, which only can be applied to collection and root element is not a collection.
Of course, I can select fields separately like:
new SomeClass(doc.Element("first").Value, doc.Element("second").Value);
But I'm really interested if is it possible to do it in one LINQ statement (using doc variable only once and creating object inside the LINQ statement)?
In other words: is it possible to create an object not in Select() method?
The root element may not be a collection, but when you parse the xml, your doc variable is a collection of elements, including root element. So you can still use Select:
string xml = #"<root><first>1</first><second>2</second></root>";
var doc = XDocument.Parse(xml);
var collectionOfSomeClass = doc.Elements()
.Select(x => new SomeClass
{ First = x.Element("first").Value,
Second = x.Element("second").Value
});

Create a List from XElements Dynamically

I am reading a bunch of XML files into a list (IEnumerable really) of XElements. Then I want to convert the XElement list (these XElements contain a bunch of child-elements) into a list of classes, so I can do later operations with the data more easily.
Now if I know in advance the structure of XElements, this would be easy; I'd just create a class that mimics the XElement structure and fill instances of it with the XElement contents. But here's the caveat; my XML file element structure is mostly similar, but there could be the odd element that has a different structure. To better illustrate the situation let me take an example.
Let's say my XML files contain a bunch of 'Person' elements. The Person elements has some common elements that will be in ALL the elements, but there are some children of Person which can be found only in some of the elements.
For example all Person elements have these mandatory children:
<Person>
<Name/>
<Age/>
<City/>
<Country/>
</Person>
But, some Person elements may contain additional children as follows:
<Person>
<Name/>
<Age/>
<City/>
<Country/>
<EyeColor/>
<Profession/>
</Person>
To make things worse, these child elements can also have mostly similar structure that occasionally varies.
So is there a way that I can go through these XElements in just one loop, and put them into an instance that is somehow dynamically created, say, based on the element names or something similar? I could create a class with all the mandatory elements and leave few additional member variables for the odd new ones, but that's not ideal for two reasons; one, it would be a waste of space, and two, there could be more child element than I have extra variables in my class.
So I'm looking for a way to create the class instances dynamically to fit the XElement structure. In other words I'd really like to mimic the element structure right down to the deepest level.
Thanks in advance!
I think the best route personally would be to get an XSD, if you cannot get that then make up a serializable class that has all the possibilities and then reference that. EG: You have two fields where one get's set sometimes and one you have never seen set but there is the potential in a spec somewhere it may happen.
So let's make up a pretend class:
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
namespace GenericTesting.Models
{
[Serializable()]
public class Location
{
[XmlAttribute()]
public int Id { get; set; }
[XmlAttribute()]
public double PercentUsed { get; set; }
[XmlElement]
public string ExtraGarbage { get; set; }
[XmlText]
public string UsedOnceInTheUniverse { get; set; }
}
}
And for the purpose of serializing/deserializing let me give extension methods for those:
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace GenericTesting
{
static class ExtensionHelper
{
public static string SerializeToXml<T>(this T valueToSerialize)
{
dynamic ns = new XmlSerializerNamespaces();
ns.Add("", "");
StringWriter sw = new StringWriter();
using (XmlWriter writer = XmlWriter.Create(sw, new XmlWriterSettings { OmitXmlDeclaration = true }))
{
dynamic xmler = new XmlSerializer(valueToSerialize.GetType());
xmler.Serialize(writer, valueToSerialize, ns);
}
return sw.ToString();
}
public static T DeserializeXml<T>(this string xmlToDeserialize)
{
dynamic serializer = new XmlSerializer(typeof(T));
using (TextReader reader = new StringReader(xmlToDeserialize))
{
return (T)serializer.Deserialize(reader);
}
}
}
}
And a simple main entry point in a console app:
static void Main(string[] args)
{
var locations = new List<Location>
{
new Location { Id = 1, PercentUsed = 0.5, ExtraGarbage = "really important I'm sure"},
new Location { Id = 2, PercentUsed = 0.6},
new Location { Id = 3, PercentUsed = 0.7},
};
var serialized = locations.SerializeToXml();
var deserialized = serialized.DeserializeXml<List<Location>>();
Console.ReadLine();
}
I know this is not exactly what you are asking for but I personally think well typed is better for XML and any third party you ever deal with should have at the very least some type of spec sheet or details on what they are giving you. Else you are losing standards. Xml should not be created from reflection or other means dynamically as it is meant if anything to enforce strict typing if anything.
if you want to just enumerate over any child element of <Person> and xml is relatively small
you could use linq to xml
var listOfElementChildNames = XDocument.Parse(xml).Element("Person")
.Elements()
.Select(e => e.Name)
.ToList();
Edit:
instead of select .Select(e => e.Name)
we could map to any class:
public class Person
{
public string Name {get;set;}
public int Age {get;set;}
public string City {get;set;}
}
var xml = #"<Person>
<Name>John</Name>
<Age>25</Age>
<City>New York</City>
</Person>";
var people = XDocument.Parse(xml).Elements("Person")
.Select(p => new Person
{
Name = p.Element("Name").Value,
Age = int.Parse(p.Element("Age").Value),
City = p.Element("City").Value
}).ToList();
Let me first apologize for the VB, but that is what I do.
If I understand what you are wanting you could use a Dictionary. I shortened your example to have fewer mandatory items, but hopefully you get the idea. Here is the person class that simply iterates the children adding them to the dictionary by their element name.
Public Class Person
Private _dict As New Dictionary(Of String, XElement)
Public Sub New(persEL As XElement)
'if the class is intended to modify the original XML
'use this declaration.
Dim aPers As XElement = persEL
'if the original XML will go away during the class lifetime
'use this declaration.
'Dim aPers As XElement =New XElement( persEL)
For Each el As XElement In aPers.Elements
Me._dict.Add(el.Name.LocalName, el)
Next
End Sub
'mandatory children are done like this
Public Property Name() As String
Get
Return Me._dict("Name").Value
End Get
Set(ByVal value As String)
Me._dict("Name").Value = value
End Set
End Property
Public Property Age() As Integer
Get
Return CInt(Me._dict("Age").Value)
End Get
Set(ByVal value As Integer)
Me._dict("Age").Value = value.ToString
End Set
End Property
'end mandatory children
Public Property OtherChildren(key As String) As String
Get
Return Me._dict(key).Value
End Get
Set(ByVal value As String)
Me._dict(key).Value = value
End Set
End Property
Public Function HasChild(key As String) As Boolean
Return Me._dict.ContainsKey(key)
End Function
End Class
Here is a simple test to see how it works
Dim onePersXE As XElement = <Person>
<Name>C</Name>
<Age>22</Age>
<Opt1>optional C1</Opt1>
<Opt2>optional C2</Opt2>
</Person>
Dim onePers As New Person(onePersXE)
onePers.Name = "new name"
onePers.Age = 42
onePers.OtherChildren("Opt1") = "new opt1 value"
onePers.OtherChildren("Opt2") = "opt 2 has new value"
As you can see there are two mandatory elements and in this case two optional children.
Here is another example to show how persons might work
Dim persons As XElement
persons = <persons>
<Person>
<Name>A</Name>
<Age>32</Age>
</Person>
<Person>
<Name>B</Name>
<Age>42</Age>
<Opt1>optional B1</Opt1>
<Opt2>optional B2</Opt2>
</Person>
</persons>
Dim persList As New List(Of Person)
For Each el As XElement In persons.Elements
persList.Add(New Person(el))
Next
Hope this at least gives you some ideas.

making XML nodes dynamically

I am calling an API and have to send a xml request in C# with data in different nodes. How can make xml dynamically and with nodes in incremental naming.
For example
<?xml version="1.0" encoding="UTF-8" ?>
<addCustomer>
<FirstName_1>ABC</FirstName_1>
<LastName_1>DEF</LastName_1>
<FirstName_2>GSH</FirstName_2>
<LastName_2>ADSF</LastName_2>
</addCustomer>
The problem is making xml nodes with incremental names like FirstName_1,FirstName_2,FirstName_3 and so on.
Would a customer have more than one FirstName and more than one LastName? If each FirstName and LastName pairs represent a different customer then your xml should look something like....
<?xml version="1.0" encoding="UTF-8" ?>
<AddCustomers>
<Customer>
<FirstName>ABC</FirstName>
<LastName>DEF</LastName>
</Customer>
<Customer>
<FirstName>GSH</FirstName>
<LastName>ASDF</LastName>
</Customer>
</AddCustomers>
If you absolutely have to do it the way that you did it in your example, I do not see any way to do this except just using a string_builder and create it yourself within a for loop while incrementing your integer to add to the end of each First and last name attributes. This is not really how xml is supposed to work.
I know your pain; having to deal with 3rd party APIs can be big pain.
Instead of using StringBuilder you can use XElement.
public void AddCustomerInfo(string firstName, string lastName, int index, XElement root)
{
XElement firstNameInfo = new XElement("FirstName_" + index);
firstNameInfo.Value = firstName;
XElement lastNameInfo = new XElement("LastName_" + index);
lastNameInfo.Value = lastName;
root.Add(firstNameInfo);
root.Add(lastNameInfo);
}
Then call the function as the following:
XElement rootElement = new XElement("addCustomer");
AddCustomerInfo("ABC", "DEF", 1, rootElement);
Put that line inside a loop and you're all set.
I think the simplest solution would be the best here:
Assuming you have a collection of Customer objects called Customers...
StringBuilder xmlForApi = new StringBuilder();
int customerCounter = 1;
foreach(Customer c in Customers)
{
xmlForApi.AppendFormat("<FirstName_{0}>{1}</FirstName_{0}><LastName_{0}>{2}</LastName_{0}>", customerCounter, c.FirstName, c.LastName)
customerCounter++;
}

Building Xml with XElement dynamically through recursion

I'm new to linq to Xml.
I have a recursive method which get as parameter XElement root which should hold the XML data in a way it will represent the correlating subtree root of a given recursion depth.
void recursiveMethod(XElement root);
To be more specific,also look at this XML example:
<start>
<Class>
<Worker>
<Name> Dan </Name>
<Phone> 123 </Phone>
<Class>
<Address>
<Street> yellow brick road </Street>
<Zip Code> 123456 </Zip Code>
</Address>
</Class>
</Worker>
</Class>
...
</start>
As you can imagine, Name is value type whereas Address is a class reference.
The Xml information should be added dynamically through reflection ( in top down approach).
To make the long story short imagine that I'm in the middle of investigating Worker Class and reached Address Class and want to "drill down", so I want to call my recursive method with the right reference of child nodes of the current Worker class as the new XElement root, so I will be able to add to it what I found by reflection in the Address Class one recursion depth below.
Note that this reference should be of XElement type.
How can I do that?
EDIT: If you have another idea of doing all this stuff but not with XElement I'll be happy to hear about also, although I prefer it with XElement parameter.
Another issue:
I've started implementing it in a naive way like iterating through all fields (variable of FieldInfo[]), and if I had encounterd value type(IsValueType) I was doing something like
root.Add(new XElement("Field",
new XElement("Type", ...),
new XElement("Variable Name", ...),
new XElement("Value", ...)));
So ,just for general knowledge:
1. Was there a way to get only the reference of a node to its decendants ,so that in lower recursion level I'll be able to do another root.Add(...) as above but this root will be a reference to children of previous root? (Which means doing the whole operation without Linq syntax)
2.I've managed to get private fields value through reflection without working with properties, is it problematic? Should I always take values through properties in reflection?
This extension method will build XElement in required format:
public static class Extensions
{
public static XElement ToXml<T>(this T obj)
{
Type type = typeof(T);
return new XElement("Class",
new XElement(type.Name,
from pi in type.GetProperties()
where !pi.GetIndexParameters().Any()
let value = (dynamic)pi.GetValue(obj, null)
select pi.PropertyType.IsPrimitive ||
pi.PropertyType == typeof(string) ?
new XElement(pi.Name, value) :
Extensions.ToXml(value)
)
);
}
}
What happens here:
We get public properties of passed object (you can add BindingFlags to filter properties).
Next I verify if property has index parameters and skip such properties.
Next I get property value as dynamic object. It's important, otherwise property value type will be inferred as object during recursive call of ToXml<T> method.
I check if property type is primitive type (int, byte, long, single, double, etc) or string
For primitive types we write element with property value. For other types (complex) we recursively start building XElement
Usage:
Worker worker = new Worker()
{
Name = "Serge",
Phone = "911",
Address = new Address() { Street = "Elm street", ZipCode = 666 }
};
XElement xml = worker.ToXml();
Result:
<Class>
<Worker>
<Name>Serge</Name>
<Phone>911</Phone>
<Class>
<Address>
<Street>Elm street</Street>
<ZipCode>666</ZipCode>
</Address>
</Class>
</Worker>
</Class>
BUT you should be careful with situations when two objects refer each other (infinite recursion will happen in this case)
In this case you can use something like dictionary or hashset to store all objects which already exist in your xml:
Type type = obj.GetType();
if (set.Contains(obj))
return new XElement("Class", new XAttribute("name", type.Name));
set.Add(obj);
return new XElement("Class", ...);

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.

Categories