How to bind XML file to List<MyClass>? - c#

I've been using the answer here
binding xml elements to model in MVC4
to bind an XML file to a model.
This works fine when the XML file has one element. I need to handle the case when there are many elements. I'd like all of those elements to go into a List.
Here's an example file:
<root>
<item>
<firstname>Tom</firstname>
</lastname>Jones</lastname>
<item>
<item>
<firstname>Jane</firstname>
</lastname>Doe</lastname>
</item>
</root>
MyXMLElements class looks like:
[Serializable]
[XmlRoot("item")]
public class MyXMLElements
{
public string first name {get;set;}
public string lastname {get;set;}
}
How I do I create a List<MyXMLElements>?

I think the easiest way is to use the XmlSerializer:
XmlSerializer serializer = new XmlSerializer(typeof(List<MyClass>));
using(FileStream stream = File.OpenWrite("filename"))
{
List<MyClass> list = new List<MyClass>();
serializer.Serialize(stream, list);
}
using(FileStream stream = File.OpenRead("filename"))
{
List<MyClass> dezerializedList = (List<MyClass>)serializer.Deserialize(stream);
}

You can achieve it this way (I'm reading XML from a file, but you can get it from other source):
XDocument xDoc = XDocument.Load(#"C:\new.xml");
List<MyXMLElements> list = (from xEle in xDoc.Descendants("item")
select new MyXMLElements() { firstname = xEle.Element("firstname").Value, lastname = xEle.Element("lastname").Value }).ToList();
You don't need XmlRoot in this case.

XElement xelement = XElement.Load("..\\..\\XML.xml");
IEnumerable<XElement> employees = xelement.Elements();
Console.WriteLine("List of all Employee Names :");
foreach (var employee in employees)
{
Console.WriteLine(employee.Element("firstname").Value + employee.Element("lastname").Value );
}

A solution is given below:
The class should look like this:
[Serializable]
[XmlType(TypeName = "item")]
public class MyXMLElements
{
public string firstname {get;set;}
public string lastname {get;set;}
}
The deserialization code is below:
using (var rdr = System.Xml.XmlReader.Create(#"input.xml"))
{
var root=new System.Xml.Serialization.XmlRootAttribute("root");
var ser = new System.Xml.Serialization.XmlSerializer(typeof(List<MyXMLElements>),root);
var list=(List<MyXMLElements>)ser.Deserialize(rdr);
}

Related

nested recursive XML to CSV (using Linq)

How should I create CSV out of List<SPSecurableObject> where SPSecurableObject is recursive
class SPSecurableObject
{
public string ObjectName {get;set;}
public List<RoleAssignments> RoleAssignments {get;set;}
public List<SPSecurableObject> ChildObjects {get;set;}
}
class RoleAssignments
{
public string PrincipalType {get;set;}
public string DisplayName {get;set;}
public string RoleDefBindings {get;set;}
}
I have XML data file (see below) and need to make CSV file out of it. Started writing query with Linq to XML and I load data into List<SPSecurableObject>, but now cannot think of the solution to write CSV which should be like this:
ObjectName,PrincipalType,DisplayName,RoleDefBindings
New Data,,,
Documents,,,
Documents2,User,John Doe,Limited Access
Documents2,Group,Group1,Full Control
XML:
<SPSecurableObject>
<ObjectName>New Data</ObjectName>
<ChildObjects>
<SPSecurableObject>
<ObjectName>Documents</ObjectName>
</SPSecurableObject>
<SPSecurableObject>
<ObjectName>Documents2</ObjectName>
<RoleAssignments>
<SPRoleAssignment>
<PrincipalType>User</PrincipalType>
<Member>
<User>
<DisplayName>John Doe</DisplayName>
</User>
</Member>
<RoleDefBindings>
<RoleName>Limited Access</RoleName>
</RoleDefBindings>
</SPRoleAssignment>
<SPRoleAssignment>
<PrincipalType>Group</PrincipalType>
<Member>
<Group>
<GroupName>Group1</GroupName>
</Group>
</Member>
<RoleDefBindings>
<RoleName>Full Control</RoleName>
</RoleDefBindings>
</SPRoleAssignment>
</RoleAssignments>
</SPSecurableObject>
</ChildObjects>
</SPSecurableObject>
My Code to form List<SPSecurableObject> is this:
void Main()
{
var xml = XDocument.Load (#"C:\temp\data.xml");
var root = xml.Elements("SPSecurableObject");
List<SPSecurableObject> result = Load(root);
}
List<SPSecurableObject> Load(IEnumerable<XElement> xml)
{
return xml.Select(x=>new SPSecurableObject(){
ObjectName = x.Elements("ObjectName").Select(y=>y.Value).FirstOrDefault(),
RoleAssignments=(from ra in x.Elements("RoleAssignments").Elements("SPRoleAssignment")
select new RoleAssignments{
PrincipalType=ra.Elements("PrincipalType").Select(y=>y.Value).FirstOrDefault(),
DisplayName=ra.Elements("PrincipalType").Select(y=>y.Value).FirstOrDefault() == "Group" ? ra.Elements("Member").Elements("Group").Elements("GroupName").Select(y=>y.Value).FirstOrDefault() : ra.Elements("Member").Elements("User").Elements("DisplayName").Select(y=>y.Value).FirstOrDefault(),
RoleDefBindings = ra.Elements("RoleDefBindings").Elements("RoleName").Select(y=>y.Value).FirstOrDefault()
}).ToList(),
ChildObjects = Load(x.Elements("ChildObjects").Elements("SPSecurableObject"))
}).ToList();
}
Thanks
Try this:
var xml = XDocument.Load("data.xml");
var sb = new StringBuilder();
foreach (var node in xml.Descendants("SPSecurableObject"))
{
var objectName = node.Element("ObjectName").Value;
if (node.Element("RoleAssignments") == null)
{
sb.Append(objectName).AppendLine(",,,");
continue;
}
var lines = node.Descendants("SPRoleAssignment")
.Select(elem => string.Join(",", elem.DescendantNodes().OfType<XText>()));
if (lines.Any())
{
foreach (var line in lines)
sb.Append(objectName).Append(',').AppendLine(line);
}
else
{
sb.Append(objectName).AppendLine(",,,");
}
}
Console.WriteLine(sb);
This will give you the result you wanted.

How to deserialize this xml doc into list

In app a get a response from vk server with information about user's playlist in xml. App throw InvalidOperationexception here
var result = (PlayList)serializer.Deserialize(reader);
DTOs:
public class PlayList
{
[XmlElement("audio")]
public List<Song> Audio { get; set; }
public PlayList()
{
Audio = new List<Song>();
}
}
public class Song
{
[XmlElement(ElementName ="id")]
public int Id { get; set; }
...
[XmlElement(ElementName ="genre_id")]
public int Genre_id { get; set; }``
}
But my code work when I delete this 3 lines from xmlfile
<response>
<count>156</count>
...
<response>
and <items list="true"> -> <items>
What I must change in my code to make it work?
var serializer = new XmlSerializer(typeof(PlayList), new XmlRootAttribute("items"));
using (var stringReader = new StringReader(xml))
using (var reader = XmlReader.Create(stringReader))
{
var result = (PlayList)serializer.Deserialize(reader);
Console.WriteLine(result.Audio[1].Title);
}
And this is an example of xml.
How do I Deserialize this XML document?
var xml =
#"<?xml version="1.0" encoding="utf-8"?>
<response>
<count>156</count>
<items list="true">
<audio>
<id>456239034</id>
<owner_id>181216176</owner_id>
<artist>Oh Wonder</artist>
<title>Technicolour Beat</title>
<duration>179</duration>
<date>1465249779</date>
<url>http://cs613222.vk.me/u285269348/audios/4ae051b98797.mp3?extra=MS3dvwutPkFx7k8rQdV3Szuh7cSwLkRcS_KpPbO9DXviMFLNNgkDAmZFWdIueioL3dDgdPUc7rch0V81KgOHYaTTSampaRcljxrJcytJYImZssivVP7DigKdcxaLoALeUatAhuHk5gXQ7TY</url>
<lyrics_id>235824304</lyrics_id>
<genre_id>1001</genre_id>
</audio>
<audio>
<id>456239033</id>
<owner_id>181216176</owner_id>
<artist>Mikky Ekko</artist>
<title>We Must Be Killers (Волчонок / Teen Wolf / 2х08) </title>
<duration>195</duration>
<date>1465249755</date>
<url>http://cs521610.vk.me/u14558277/audios/c2daca7b2b6f.mp3?extra=z9VPdKf6v- n7zkIfZ_6ej-RZSjlIjAr_qYmVp4F-zI1Z3ZXgVtOUElovlOiSOgSuKbFC0e0ahac8XU-AxNtfEYYPe5gcejSotr84mHi0LQ2L-b0BPWP2cYn5Yy44YN4FLPNKq0Ow8vMKFn0</url>
<lyrics_id>26311225</lyrics_id>
</audio>
</items>
</response>";
You need to let it know its an array, I think using typeof(PlayList[]) instead of typeof(PlayList)
You can just skip unnecessary nodes with the XmlReader.
using (var reader = XmlReader.Create(stringReader))
{
reader.ReadToFollowing("items");
var result = (PlayList)serializer.Deserialize(reader);

Add dynamically xml tag to serialized object without modifying the object

I have an object serializable like this :
[XmlRoot("MyObject")]
[Serializable]
public class MyObject
{
public MyObject()
{
}
[XmlAttribute("name")]
public string Name { get; set; }
}
Right now I am able to export this object in an xml file with the following code :
public byte[] Export(MyObject myObject)
{
XmlSerializer serializer = new XmlSerializer(typeof(MyObject));
byte[] value = null;
using (MemoryStream stream = new MemoryStream())
{
StreamWriter writer = new StreamWriter(stream);
serializer.Serialize(writer, myObject);
value = stream.ToArray();
}
return value;
}
For the moment, my exported xml is like this :
<MyObject Name="nameData">
I would like to export other data (a list of name related to myObject and obtained from another service) with this. But I cannot modify my MyObject class.
This is how I get the additionnal data :
public XElement GetExtraData(MyObject myObject)
{
List<string> datas = service.GetData(myObject);
var otherDatas = new XElement("otherDatas");
foreach (var data in datas)
{
var dataElement = new XElement("data");
var valueAttribute = new XAttribute("Value", data);
dataElement.Add(valueAttribute);
}
otherDatas.Add(dataElement);
}
I would like to append this otherDatas element to my previous element in order to obtain the following :
<MyObject Name="nameData">
<data Value="valueData">
<data Value="valueData">
<data Value="valueData">
But I don't see how, as this new method returns me a xElement created by me, and the natively .net serialization returns me an array of bytes.
Add the XElement returned from GetExtraData as Nodes to the original xml:
var doc = new XDocument(GetExtraData(new MyObject { Name = "someOtherName" }));
var byt = Export(new MyObject { Name = "nameData" });
using (var stream = new MemoryStream(byt))
{
var element = new XElement(XElement.Load(stream));
element.Add(doc.Nodes());
}
And save element

String builder to XML traverse through each node

Hi all I am having an XML data which is formed in StringBuilder as follows
StringBuilder sb = new StringBuilder();
sb.Append("<?xml version=\"1.0\" encoding=\"utf-16\"?>");
sb.Append("<TEST>"
+ "<DEMO><CONTENTINFO name=\"Nani\" receiver=\"Lucky\""
+ "/></DEMO></TEST>");
XmlDocument XMLDocument = new XmlDocument();
XMLDocument.LoadXml(sb.ToString());
XmlNodeList nodeList = XMLDocument.FirstChild.ChildNodes;
foreach (XmlNode node in nodeList)
{
}
I tried using XMLDocument to traverse through child nodes to get the data I need to split the data so that it should give name=Nani and receiver=lucky or store the key and value in a dictionary like dic.Add("name","nani") and dic.Add("receiver","lucky") . So can some one help me how to sort it out
If you prefer having a strong set of class instances being formed then use the XmlSerializer and create classes to represent each of the levels of your XML structure.
[XmlRoot("TEST")]
public class Test
{
[XmlElement(Name = "DEMO")]
public Demo Demo
{
get;
set;
}
}
public class Demo
{
[XmlElement("CONTENTINFO")]
public ContentInfo ContentInfo
{
get;
set;
}
}
public class ContentInfo
{
[XmlAttribute(Name = "name")]
public string Name
{
get;
set;
}
[XmlAttribute(Name = "receiver")]
public string Reciever
{
get;
set;
}
}
XmlSerializer serializer = new XmlSerializer(typeof(Test));
serializer.Serialize(....);
Test testInstance = serializer.Deserialize(....);
... etc.
The above code has not been tested but should give you the gist.
Why are you using a StringBuilder to generate your XML? There are much better ways:
var root = new XDocument();
var test = new XElement("TEST");
var demo = new XElement("DEMO");
var contentInfo = new XElement("CONTENTINFO",
new XAttribute("name", "Nani"),
new XAttribute("receiver", "Lucky"));
demo.Add(contentInfo);
test.Add(demo);
root.Add(test);
To pull out the values you need into a dictionary you can use the following LINQ query:
var foo = root.Descendants("DEMO").Elements("CONTENTINFO")
.SelectMany(x => x.Attributes())
.ToDictionary(x => x.Name.ToString(), x => x.Value.ToString());
This will give you a dictionary looking like this:
Key: name =
Value: Nani,
Key: receiver = Value: Lucky

XML Deserialization without specifying XmlRootAttribute

here is my xml :
<connections total="2" >
<person>
<id>ohKiUAZWz2</id>
<first-name>ミ★нιяαℓ</first-name>
<last-name>§|-|ä|-|»♥«</last-name>
<headline>--</headline>
</person>
<person>
<id>LmgYe-Nl2a</id>
<first-name>kunal</first-name>
<last-name>b</last-name>
<headline>Student at MscIT,Surat</headline>
</person>
</connection>
from code behind :
List<LinkWall> LinkWallList = new List<LinkWall>();
XmlNodeList xmlnode = doc.GetElementsByTagName("person");
foreach (XmlElement ele in xmlnode)
{
XmlRootAttribute xr = new XmlRootAttribute("person");
XmlSerializer mySerializer = new XmlSerializer(typeof(LinkWall),xr);
StringReader re = new StringReader(ele.InnerXml);
LinkWallList.Add((LinkWall)mySerializer.Deserialize(re));
}
here is my class definition :
[XmlRoot("person")]
public class LinkWall
{
public LinkWall()
{ }
[XmlElement(ElementName = "id")]
public string id { get; set; }
[XmlElement(ElementName = "first-name")]
public string firstName { get; set; }
[XmlElement(ElementName = "last-name")]
public string lastName { get; set; }
[XmlElement(ElementName = "headline", IsNullable=true)]
public string headline { get; set; }
}
but when i try to deserialize. it show me error like : {"There are multiple root elements."}
is there any solution or alternative for specifying XmlRootAttribute ?
thanks in advance, Milan Mendpara
I think your issue is with this line:
StringReader re = new StringReader(ele.InnerXml);
Change it to:
StringReader re = new StringReader(ele.OuterXml);
The reason is the InnerXml property will return all of the child nodes but not the parent node. OuterXml will include your parent person node too.
i.e. InnerXml has no root element (well, it has many!):
<id>ohKiUAZWz2</id>
<first-name>?????al</first-name>
<last-name>§|-|ä|-|»?«</last-name>
<headline>--</headline>
OuterXml is as expected:
<person>
<id>ohKiUAZWz2</id>
<first-name>?????al</first-name>
<last-name>§|-|ä|-|»?«</last-name>
<headline>--</headline>
</person>
There is also no real need to use the XmlSerializer constructor you are using. Try:
XmlSerializer mySerializer = new XmlSerializer(typeof(LinkWall));
Instead.
Try OuterXml instead of InnerXML
StringReader re = new StringReader(ele.OuterXml);
I believe you should make a class structured like your xml file and deserialize your xml file into an instance of this class.
MyClass myObject = new MyClass;
XmlSerializer ser = new XmlSerializer(myObject.GetType());
using (FileStream fs = new FileStream(FilePath, FileMode.Open))
{
XmlTextReader reader = new XmlTextReader(fs);
myObject = (MyClass)ser.Deserialize(reader);
}
This code runs faster and then you will be able to do wathever you want with the data inside your object.

Categories

Resources