Deserialization from XML to List Object - c#

I am doing the program in convert XML file to List Objects. I have successfully done serialization from List to XML .but I have an problem on doing deserialization. Please anyone tell me what's the wrong i have done in this code.
This is my XML code.
<?xml version="1.0"?>
<Contact_x0020_Form xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Contact>
<Contact>
<Id>1</Id>
<Name>vicky1kumar</Name>
<Phone>248847227</Phone>
</Contact>
<Contact>
<Id>2</Id>
<Name>vicky1kumar2kumar</Name>
<Phone>725228355</Phone>
</Contact>
<Contact>
<Id>3</Id>
<Name>vicky1kumar2kumar3kumar</Name>
<Phone>2032848116</Phone>
</Contact>
<Contact>
<Id>4</Id>
<Name>vicky1kumar2kumar3kumar4kumar</Name>
<Phone>853938969</Phone>
</Contact>
<Contact>
<Id>5</Id>
<Name>vicky1kumar2kumar3kumar4kumar5kumar</Name>
<Phone>530646891</Phone>
</Contact>
</Contact>
<Id>0</Id>
</Contact_x0020_Form>
This is my Class for convert XML to List Object
public class Converter
{
public static T XmlToObject<T>(string xml)
{
using (var xmlStream = new StringReader(xml))
{
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(XmlReader.Create(xmlStream));
}
}
public static List<T> XmlToObjectList<T>(string xml, string nodePath)
{
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(xml);
var returnItemsList = new List<T>();
foreach (XmlNode xmlNode in xmlDocument.SelectNodes(nodePath))
{
returnItemsList.Add(XmlToObject<T>(xmlNode.OuterXml));
}
return returnItemsList;
}
}
And this is my DEserialization code...
List<string> decont = new List<string>();
decont = Converter.XmlToObjectList<string>(#"C:\vignesh\serialization\xmlserialize\XmlSerializeContact.xml","//Contact");
foreach (var item in decont)
{
Console.WriteLine(decont);
}
I got this error:
Data at the root level is invalid. Line 1, position 1.

Data at the root level is invalid. Line 1, position 1.
To address this first error, you must understand the cause. The issue is LoadXml accepts an xml string; whereas you are passing a path to an Xml file. You need to use Load instead of LoadXml.
That said, there are a lot of other things you need to correct. The serialized XML provided in your question seems to be incorrect--e.g. the Contact node is its own parent. Thus, your node selection is giving you the entire Xml. (Did you mean to define Contacts node to be the parent of the Contact list?)
<Contacts>
.. <Contact>

First, your xml should look like this:
<?xml version="1.0"?>
<Contact_x0020_Form>
<Contacts>
<Contact>
<Id>1</Id>
<Name>vicky1kumar</Name>
<Phone>248847227</Phone>
</Contact>
<Contact>
<Id>2</Id>
<Name>vicky1kumar2kumar</Name>
<Phone>725228355</Phone>
</Contact>
</Contacts>
</Contact_x0020_Form>
Second, define serialization classes something like :
[XmlRoot(ElementName = "Contact_x0020_Form")]
public class Root
{
[XmlElement("Contacts")]
public Contacts contacts{get;set;}
}
public class Contacts
{
public List<Contact> contacts {get;set;}
}
public class Contact
{
[XmlElement("Id")]
public int Id {get;set;}
[XmlElement("Name")]
public string Name {get;set;}
[XmlElement("Phone")]
public string Phone {get;set;}
}
and your helper classes
public static class serialize
{
public static T Deserialize<T>(string path)
{
T result;
using (var stream = File.Open(path, FileMode.Open))
{
result = Deserialize<T>(stream);
}
return result;
}
public static void Serialize<T>(T root, string path)
{
using (var stream = File.Open(path, FileMode.Create))
{
var xmlSerializer = new XmlSerializer(typeof(T));
xmlSerializer.Serialize(stream, root);
}
}
public static T Deserialize<T>(Stream stream)
{
var xmlSerializer = new XmlSerializer(typeof(T));
return (T)xmlSerializer.Deserialize(stream);
}
}
And, finally your func:
static void Main()
{
var a = serialize.Deserialize<Root>("input.xml"); //xml file name given here.
Console.WriteLine(a.contacts);
}
This is how you should proceed. Then on, you can get the list of objects that you want.

use this before calling xml data in deserialization
xml = Regex.Replace(xml, "<\\?xml.*?>", String.Empty);
here xml is your xml data.

Related

c# Write/Read complex IXmlSerializable class with XmlSerializer fields

I have a class which I would like to serialize. However, this class implements IEnumerator, so I have to make it IXmlSerializable. My class looks like this:
public class Pipeline : IEnumerator<Node>, IEnumerable<Node>, IXmlSerializable
{
public string Name { get; set; }
public List<Trigger> triggers;
public Node root;
//Methods...
}
The WriteXml-method I came up with is quite straight forward and writes the object (Pipeline), which is representing nodes, some metadata (name, id, etc.) and the edges between the nodes as a file:
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("Name", Name);
List<Node> nod = new List<Node>(this);
var ser1 = new XmlSerializer(typeof(List<Node>), GetLoadedTypes().ToArray());
ser1.Serialize(writer, nod);
var ser2 = new XmlSerializer(typeof(List<Edge>));
ser2.Serialize(writer, GetEdges());
}
The produced XML-file looks ok to me:
<?xml version="1.0" encoding="utf-8"?>
<Pipeline Name="TestPipeline">
<ArrayOfNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Node xsi:type="TestTrigger" ID="63248778" Name="yesyes" />
<Node xsi:type="Do" ID="32368095">
<variables>
<string>hello</string>
</variables>
<Actions>x => Console.WriteLine($"{x}")</Actions>
</Node>
</ArrayOfNode>
<ArrayOfEdge xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Edge From="63248778" To="32368095" />
<Edge From="32368095" To="" />
</ArrayOfEdge>
</Pipeline>
But now my troubles are starting: How should the ReadXML-method looks like? What I came up with, ends in the error InvalidOperationException: <Pipeline xmlns=''> was not expected. in the line Node[] nods = (Node[])ser1.Deserialize(reader);.
public void ReadXml(XmlReader reader)
{
Name = reader.GetAttribute("Name");
var ser1 = new XmlSerializer(typeof(Node[]));
Node[] nods = (Node[])ser1.Deserialize(reader);
//-- load the Edges as well
}
Because the two arrays are quit complex (list of derivatives of Node) and their fields can change in future, I don't want to do this by reading it elementwise and cast it to my the needed objects.
What is the correct solution to read the XML-File again?
As György pointed out in his comment, the missing part was the Read/MoveTo-Command. Here a working example with a nested XmlSerializer:
public void ReadXml(XmlReader reader)
{
Name = reader.GetAttribute("Name");
reader.Read(); // Check if read was successful
var ser1 = new XmlSerializer(typeof(Node[]), Utils.Utils.GetNodeTypes());
Node[] nods = (Node[])ser1.Deserialize(reader);
var ser2 = new XmlSerializer(typeof(Edge[]), new Type[] { typeof(Edge) });
Edge[] edges = (Edge[])ser2.Deserialize(reader);
// Do something with your elements
}

c# loop through xml child nodes and deserialize

I'm trying to loop into an XmlDocument to serialize objects. Let's suppose an simple xml :
<?xml version="1.0" encoding="iso-8859-15"?>
<root>
<message>
<id>1</id>
<text>test</text>
</message>
<message>
<id>2</id>
<text>test 2</text>
</message>
</root>
So this is my c# program :
class Program
{
static void Main(string[] args)
{
XmlReaderSettings readerSettings = new XmlReaderSettings();
readerSettings.IgnoreComments = true;
XmlSerializer serializer = new XmlSerializer(typeof(Message));
XmlReader xmlReader = XmlReader.Create(#"..\..\test.xml");
XmlDocument doc = new XmlDocument();
doc.Load(xmlReader);
foreach(XmlElement element in doc.DocumentElement.ChildNodes)
{
Console.WriteLine($"id : {element.SelectSingleNode("id").InnerText}, message : {element.SelectSingleNode("text").InnerText}");
Message message = (Message)serializer.Deserialize(XmlReader.Create(element.OuterXml.ToString()));
}
Console.ReadLine();
}
}
public class Message
{
public int id;
public string text;
}
but i got an error Illegal characters in path, but the print is okay, what's wrong ? and is there a way to serialize directly the XmlElement without go through the tostring() ?
Ok, i found the problem. The serializer is looking for a tag named Message like the name of the class but the tag was message with an lowercase m. It is still possible to differentiate the name of the class and the label of the tag and associate them with a decorator as :
[XmlRoot(ElementName = "message")]
public class Message
{
public int id { get; set; }
public string? text { get; set; }
}

how do i deserialize this xml?

I need to deserialize the xml below. I want to use xmlserializer because I am (more) familiar with it.
I believe this xml is not constructed correctly however I cannot change it.
The below represents a list of category objects. When I try to deserialize using
xmlserializer(typeof(List<Category>))
I get this error: "categories xmlns='' is not expected"
<?xml version="1.0" encoding="utf-8" ?>
<categories>
<category id="16" name="Exports" parent_id="13"/>
<category id="17" name="Imports" parent_id="13"/>
<category id="3000" name="Income Payments & Receipts" parent_id="13"/>
<category id="125" name="Trade Balance" parent_id="13"/>
<category id="127" name="U.S. International Finance" parent_id="13"/>
</categories>
I don't mind making some kind of dummy class to deserilize these if that is what I have to do.
Here is my Category Class
[XmlType("category")]
public class Category
{
[XmlAttribute("id")]
public int ID { get; set; }
[XmlAttribute("parent_id")]
public int ParentID { get; set; }
[XmlAttribute("name")]
public string Name { get; set; }
}
My code:
XmlSerializer serializer = new XmlSerializer(typeof(List<Category>));
StringReader reader = new StringReader(xml);
List<Category> obj = null;
using (System.Xml.XmlReader xmlReader = System.Xml.XmlReader.Create(reader))
{
obj = (List<Category>)serializer.Deserialize(xmlReader);
}
return obj;
You can just pass in the XmlRootAttribute into the serializer for the "categories" part.
BUT... you must remove the "&" from your xml because its not valid
XmlSerializer serializer = new XmlSerializer(typeof(List<Category>), new XmlRootAttribute("categories"));
using (FileStream fileStream = new FileStream(#"C:\Test.xml", FileMode.Open, FileAccess.Read, FileShare.Read))
{
var test = serializer.Deserialize(fileStream);
}
Here is your method working with a String.Replace to sort out the "&"
private List<Category> GetCategories(string xmlData)
{
List<Category> obj = null;
XmlSerializer serializer = new XmlSerializer(typeof(List<Category>), new XmlRootAttribute("categories"));
StringReader reader = new StringReader(xmlData.Replace("&","&"));
using (System.Xml.XmlReader xmlReader = System.Xml.XmlReader.Create(reader))
{
obj = (List<Category>)serializer.Deserialize(xmlReader);
}
return obj;
}
Try to make a categories class that will contain your List<Category> like this:
[XmlRoot("categories")]
public class Categories
{
public Categories()
{
Items = new List<User>();
}
[XmlElement("category")]
public List<Category> Items {get;set;}
}
You can than create a serializer like this:
XmlSerializer serializer = new XmlSerializer(typeof(Categories));
Do you have an XSD that this XML should conform to? If so, you can generate the required code using:
"xsd your.xsd /classes"

Serialize List<T> to XML, and reverse the XML to List<T>

Does anyone know how I (or if it's possible to) reverse the XML I'm creating below
[Serializable()]
public class CustomDictionary
{
public string Key { get; set; }
public string Value { get; set; }
}
public class OtherClass
{
protected void BtnSaveClick(object sender, EventArgs e)
{
var analysisList = new List<CustomDictionary>();
// Here i fill the analysisList with some data
// ...
// This renders the xml posted below
string myXML = Serialize(analysisList).ToString();
xmlLiteral.Text = myXML;
}
public static StringWriter Serialize(object o)
{
var xs = new XmlSerializer(o.GetType());
var xml = new StringWriter();
xs.Serialize(xml, o);
return xml;
}
}
The xml rendered
<?xml version="1.0" encoding="utf-16"?>
<ArrayOfCustomDictionary xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<CustomDictionary>
<Key>Gender</Key>
<Value>0</Value>
</CustomDictionary>
<CustomDictionary>
<Key>Height</Key>
<Value>4</Value>
</CustomDictionary>
<CustomDictionary>
<Key>Age</Key>
<Value>2</Value>
</CustomDictionary>
</ArrayOfCustomDictionary>
Now, after a few hours of Googling and trying I'm stuck (most likely my brain have some vacation already). Can anyone help me how to reverse this xml back to a List?
Thanks
Just deserialize it:
public static T Deserialize<T>(string xml) {
var xs = new XmlSerializer(typeof(T));
return (T)xs.Deserialize(new StringReader(xml));
}
Use it like this:
var deserializedDictionaries = Deserialize<List<CustomDictionary>>(myXML);
XmlSerializer.Deserialize

Changing the XML structure generated by XmlSerializer in C#

I have classes as follows
namespace Coverage {
public class ClassInfo {
public string ClassName;
public int BlocksCovered;
public int BlocksNotCovered;
public ClassInfo() {}
public ClassInfo(string ClassName, int BlocksCovered, int BlocksNotCovered)
{
this.ClassName = ClassName;
this.BlocksCovered = BlocksCovered;
this.BlocksNotCovered = BlocksNotCovered;
}
}
public class Module {
public List<ClassInfo> ClassInfoList;
public int BlocksCovered;
public int BlocksNotCovered;
public string moduleName;
public Module()
{
ClassInfoList = new List<ClassInfo>();
BlocksCovered = 0;
BlocksNotCovered = 0;
moduleName = "";
}
With the following serializer code
XmlSerializer SerializerObj = new XmlSerializer(typeof(Module));
// Create a new file stream to write the serialized object to a file
TextWriter WriteFileStream = new StreamWriter(#"test.xml");
SerializerObj.Serialize(WriteFileStream, report);
WriteFileStream.Close();
I can get the following XML file.
<?xml version="1.0" encoding="utf-8"?>
<Module xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ClassInfoList>
<ClassInfo>
<ClassName>Fpga::TestMe</ClassName>
<BlocksCovered>4</BlocksCovered>
<BlocksNotCovered>8</BlocksNotCovered>
</ClassInfo>
<ClassInfo>
<ClassName>Fpga::TestMe2</ClassName>
<BlocksCovered>4</BlocksCovered>
<BlocksNotCovered>8</BlocksNotCovered>
</ClassInfo>
</ClassInfoList>
<BlocksCovered>8</BlocksCovered>
<BlocksNotCovered>16</BlocksNotCovered>
<moduleName>helloclass.exe</moduleName>
</Module>
Q1 : How can I remove the xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://... to have simple element <Module>..</Module>?
Q2 : The XML element name is exactly the same as the class name or variable name. Can I replace it with my own?
Q3 : Can I remove the outer <ClassInfoList>?
For example, how can I generate the XML as follows:
<?xml version="1.0" encoding="utf-8"?>
<Module>
<Class>
<ClassName>Fpga::TestMe</ClassName>
<BlocksCovered>4</BlocksCovered>
<BlocksNotCovered>8</BlocksNotCovered>
</Class>
<Class>
<ClassName>Fpga::TestMe2</ClassName>
<BlocksCovered>4</BlocksCovered>
<BlocksNotCovered>8</BlocksNotCovered>
</Class>
<BlocksCovered>8</BlocksCovered>
<BlocksNotCovered>16</BlocksNotCovered>
<moduleName>helloclass.exe</moduleName>
</Module>
(btw, it doesn't tie to the question, but you should aim to avoid public fields, for lots of reasons covered in many stackoverflow questions)
Q3: Simply:
[XmlElement("Class")]
public List<ClassInfo> ClassInfoList;
Q2 re the top level name; you can use
[XmlRoot("somethingFun")]
public class Module { ... }
Q2 re member names:
[XmlElement("blocks")]
public int BlocksCovered;
(see also [XmlAttribute(...)])
Q1 Removing the xsi etc can be done with XmlSerializerNamespaces:
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
var ser = new XmlSerializer(typeof(Module));
ser.Serialize(destination, module, ns);

Categories