Load xml from file - c#

Been looking around alot to find answers to my problem. I found a great way to convert my List to an xml file, but I also want to get it back to a list.
My current xml file looks like this:
<Commands>
<Command command="!command1" response="response1" author="niccon500" count="1237"/>
<Command command="!command2" response="response2" author="niccon500" count="1337"/>
<Command command="!command3" response="response3" author="niccon500" count="1437"/>
</Commands>
Generated with:
var xml = new XElement("Commands", commands.Select(x =>
new XElement("Command",
new XAttribute("command", x.command),
new XAttribute("response", x.response),
new XAttribute("author", x.author),
new XAttribute("count", x.count))));
File.WriteAllText(file_path, xml.ToString());
So, how would I get the info back from the xml file?

You can use the following piece of code:
XDocument doc = XDocument.Load(#"myfile.xml");
to load the XML file into an XDocument object.
Then you can use .Descendants("Commands").Elements() to access all elements contained inside the <Commands> element and add them to a list:
List<Command> lstCommands = new List<Command>();
foreach(XElement elm in doc.Descendants("Commands").Elements())
{
lstCommands.Add(new Command
{
command = elm.Attribute("command").Value,
response = elm.Attribute("response").Value,
author = elm.Attribute("author").Value,
count = int.Parse(elm.Attribute("count").Value)
});
}

When using xml for persistent storage, start with defining POC classes and decorate them with proper attributes - like
[XmlType("Command")]
public class CommandXml
{
[XmlAttribute("command")]
public string Command { get; set; }
[XmlAttribute("response")]
public string Response { get; set; }
[XmlAttribute("author")]
public string Author { get; set; }
[XmlAttribute("count")]
public int Count { get; set; }
}
[XmlRoot("Commands")]
public class CommandListXml : List<CommandXml>
{
}
And then de-serialization is simple as:
var txt = #"<Commands>
<Command command=""!command1"" response=""response1"" author=""niccon500"" count=""1237""/>
<Command command=""!command2"" response=""response2"" author=""niccon500"" count=""1337""/>
<Command command=""!command3"" response=""response3"" author=""niccon500"" count=""1437""/>
</Commands>";
CommandListXml commands;
using (var reader = new StringReader(txt))
{
var serializer = new XmlSerializer(typeof(CommandListXml));
commands = (CommandListXml)serializer.Deserialize(reader);
}

You can use XDocument.Load method to load the document and then create command list using LINQ to XML syntax :
XDocument doc = XDocument.Load(file_path);
var commands = doc.Root.Elements("Commands").Select(e =>
new Command()
{
command = e.Attribute("command").Value,
response = e.Attribute("response").Value,
author = e.Attribute("author").Value,
count= int.Parse(e.Attribute("count").Value)
}).ToList();

Related

C# deserialize XML to a custom class

I have one single configuration XML file which can be deserialised into different instances, each of which takes only a portion of this XML.
class A
{
public string A { get; set; }
}
class B
{
public string B { get; set; }
}
public static T Xml2Cls<T>(string filename)
{
XDocument doc = XDocument.Load(filename);
var root = new XMLRootAttribte(doc.Root.Name.LocalName);
var serializer = new XMLSerializer(typeof(T), root);
using (var reader = doc.Root.CreateReader())
{
return (T)serializer .Deserialize(reader);
}
}
// if I keep calling the following for say 1000 times
// all these instances don't seem to get garbage collected
var a = Xml2Cls<A>("a.xml");
var b = Xml2Cls<B>("a.xml");
<root>
<a>...</a>
<b>...</b>
</root>
Any ideas why those instantiated objects are not garbage collocted? I tried moving the return out of the using block but it doesn't make a difference.

How to make a specified xml format file

I'm totally new to C#. I have to write a xml file which similarly looks like.
<DATA_SET SampleSize="5">
<DATA SampleID="1" IW="0.889" SL="24.24" PO="117" />
<DATA SampleID="2" IW="0.896" SL="24.41" PO="119" />
<DATA SampleID="3" IW="0.922" SL="24.3" PO="125" />
<DATA SampleID="4" IW="0.94" SL="24.24" PO="129" />
<DATA SampleID="5" IW="0.987" SL="24.32" PO="127" />
</DATA_SET>
What I have to tried is,
if (oneSet.Length == 5)
{
qtm.SampleID = oneSet[0];//ROD
qtm.SL = oneSet[1];//SIZEmmL
qtm.SY = oneSet[2];//OVALmm
qtm.RP = oneSet[3];//ROUND%
qtm.VN = oneSet[4];//VENT%
lstQTM.Add(qtm);
isSet = true;
}
Here QTM is a class, through this class I have made list. SampleID,SL,SY,RP,VN are the methods of QTM class file.
By using above code I added those into a list.
using (FileStream fs = new FileStream("D:\\B.xml", FileMode.Create))
{
new XmlSerializer(typeof(List<QTM>)).Serialize(fs, lstQTM);
}
My out put is
<QTM>
<SampleID>ROD</SampleID>
<SL>WTg</SL>
<SY>SIZEmmL</SY>
<RP>OVALmm</RP>
<VN>PDmm</VN>
</QTM>
<QTM>
<SampleID>1</SampleID>
<SL>0.740</SL>
<SY>23.94</SY>
<RP>0.28</RP>
<VN>357</VN>
</QTM>
<QTM>
<SampleID>2</SampleID>
<SL>0.751</SL>
<SY>23.98</SY>
<RP>0.29</RP>
<VN>368</VN>
</QTM>
<QTM>
<SampleID>3</SampleID>
<SL>0.733</SL>
<SY>23.95</SY>
<RP>0.39</RP>
<VN>351</VN>
</QTM>
<QTM>
<SampleID>4</SampleID>
<SL>0.747</SL>
<SY>23.99</SY>
<RP>0.32</RP>
<VN>363</VN>
</QTM>
<QTM>
<SampleID>5</SampleID>
<SL>0.734</SL>
<SY>23.96</SY>
<RP>0.23</RP>
<VN>356</VN>
</QTM>
<QTM>
<SampleID>6</SampleID>
<SL>0.742</SL>
<SY>23.89</SY>
<RP>0.64</RP>
<VN>365</VN>
</QTM>
</ArrayOfQTM>
How can I do this. could you please give me any idea.
Here's an example class structure which preserves the structure of the QTM class you already have, but still gives the XML that you want to get.
[XmlRoot("DATA_SET")]
public class QTMCollection
{
[XmlElement("DATA")]
public List<QTM> QTMs { get; set; }
[XmlAttribute("SampleSize")]
public int SampleSize { get { return QTMs.Count; } set { return; } }
}
public class QTM
{
[XmlAttribute]
public int SampleId { get; set; }
[XmlAttribute]
public decimal IW { get; set; }
[XmlAttribute]
public decimal SL { get; set; }
[XmlAttribute]
public int PO { get; set; }
}
We need an outer class structure here - mainly driven by the SampleSize attribute on the outer DATA_SET element, otherwise we wouldn't have to have it.
There are some quirks here - XmlSerializer will only serialize properties that have a getter and setter, which is why I have the otherwise unnecessary setter on SampleSize. The XmlRoot, XmlAttribute and XmlElement serve to tell the serializer what to call your classes and properties in the XML.
Here is an example of how to invoke the serializer to get an xml exactly like your example:
QTMCollection collection = new ConsoleApplication135.QTMCollection()
{
QTMs = new List<QTM>
{
new QTM() { SampleId = 1, IW = 0.0889M, SL = 24.24M, PO = 117 },
new QTM() { SampleId = 2, IW = 0.896M, SL = 24.41M, PO = 119 },
new QTM() { SampleId = 3, IW = 0.922M, SL = 24.3M, PO = 125 },
new QTM() { SampleId = 4, IW = 0.94M, SL = 24.24M, PO = 129 },
new QTM() { SampleId = 5, IW = 0.987M, SL = 24.32M, PO = 127 }
}
};
using (FileStream fileStream = new FileStream(#"c:\temp\B.xml", FileMode.Create))
{
using (var writer = XmlWriter.Create(fileStream, new XmlWriterSettings() { OmitXmlDeclaration = true, Indent = true }))
{
var serializer = new XmlSerializer(typeof(QTMCollection));
serializer.Serialize(writer, collection, new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty }));
}
}
The reason for using XmlWriterSettings (and so the XmlWriter) here is to omit the Xml declaration at the top of the XML that would otherwise appear:
<?xml version="1.0" encoding="utf-8"?>
And the purpose of this:
new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty })
Is to avoid the default namespace declaration on the root element that would otherwise appear:
<DATA_SET xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" SampleSize="5">
Whereas with both those things, the output will be an unadorned XML like this:
<DATA_SET SampleSize="5">
<DATA SampleId="1" IW="0.0889" SL="24.24" PO="117" />
<DATA SampleId="2" IW="0.896" SL="24.41" PO="119" />
<DATA SampleId="3" IW="0.922" SL="24.3" PO="125" />
<DATA SampleId="4" IW="0.94" SL="24.24" PO="129" />
<DATA SampleId="5" IW="0.987" SL="24.32" PO="127" />
</DATA_SET>
In your C# classes you can add attributes to your member variables that will change how XmlSerializer will behave. For example, the following will make the SampleID be serialised as an attribute rather than an element:
public class QTM {
[XmlAttribute("SampleID")]
public int SampleID;
}
See the documentation for more information here
Update
As noted by Rand Random, I misread the original question. Therefore, be awate of it. I quote the comment for future reference.
As note by the comment, the OP is indeed aware of the mechanism, but
didn't know that he needs a root object and that he can change a
XmlElement to XmlAttribute by declaring it with attributes
If you want to read an XML document without dirtying your hands, you can use XmlSerializer.
It is super easy. First you create POCO (classes with only properties) that mimick the XML file you want to read. Start from the root:
[XmlRoot("DATA_SET ")]
public sealed class MyRoot
{
[XmlElement("sampleSize")]
public int SampleSize { get; set; }
[XmlArrayItem("data")]
public List<MyData> Data { get; set; }
}
The above code models the root. It is as verbose as possible, in order to understand what you are effectively doing.
Then, you model the inner node:
[Serializable]
public sealed class MyData
{
[XmlAttribute("SampleID")]
public int SampleId { get; set; }
[XmlAttribute("IW")]
public double WhateverThisIs1 { get; set; }
[XmlAttribute("SL")]
public double WhateverThisIs2 { get; set; }
[XmlAttribute("PO")]
public int WhateverThisIs3 { get; set; }
}
The above code declares the properties with the names you want in C#, mapped to the names you expect in your xml file. As you can see they differ.
Finally, you can read your file in this way:
var serializer = new XmlSerializer(typeof(MyRoot));
using (var reader = new StreamReader(fileName))
{
var root = (MyRoot)serializer.Deserialize(reader);
// do something with root.
}
Note: You might have to adjust the root node to properly handle its children node, in case the hierarchy is more complex.
You can read more at MSDN

C# serialize object to XML with element containing XML text without escaping

I searched and tried some attributes but none of them worked for my below scenario:
public class ObjSer
{
[XmlElement("Name")]
public string Name
{
get; set;
}
}
//Code to serialize
var obj = new ObjSer();
obj.Name = "<tag1>Value</tag1>";
var stringwriter = new System.IO.StringWriter();
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
serializer.Serialize(stringwriter, obj);
Output would be as follows:
<ObjSer><Name><tag1>Value</tag1></Name></ObjSer>
But I need output as:
<ObjSer><Name><tag1>Value</tag1></Name></ObjSer>
Scenario 2: In some cases I need to set:
obj.Name = "Value";
Is there any attribute or method I can override to make it possible?
You can't with default serializer. XmlSerializer does encoding of all values during serialization.
If you want your member to hold xml value, it must be XmlElement. Here is how you can accomplish it
public class ObjSer
{
[XmlElement("Name")]
public XmlElement Name
{
get; set;
}
}
var obj = new ObjSer();
// <-- load xml
var doc = new XmlDocument();
doc.LoadXml("<tag1>Value</tag1>");
obj.Name = doc.DocumentElement;
// --> assign the element
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
serializer.Serialize(Console.Out, obj);
Output:
<?xml version="1.0" encoding="IBM437"?>
<ObjSer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>
<tag1>Value</tag1>
</Name>
</ObjSer>
UPDATE:
In case if you want to use XElement instead of XmlElement, here is sample on how to do it
public class ObjSer
{
[XmlElement("Name")]
public XElement Name
{
get; set;
}
}
static void Main(string[] args)
{
//Code to serialize
var obj = new ObjSer();
obj.Name = XDocument.Parse("<tag1>Value</tag1>").Document.FirstNode as XElement;
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
serializer.Serialize(Console.Out, obj);
}
No, you can't. It is the natural and usual behaviour of the xml serializer. The serializer doesn't have to handle within the XML strings. So, it escapes the xml as expected.
You should decode the escaped string again while deserializing it.
If you want to add dynamically elements in the XML I suggest you to use Linq to XML and you can create tag1 or another kind of elements easily.
I would suggest serializing to an XDocument then converting that to a string and manually unescaping the desired part and writing it to a file. I would say this would give you the least headache it shouldn't be more than a couple lines of code. If you need it I can provide some code example if needed.
I found one more way of changing the type
public class NameNode
{
public string tag1
{
get; set;
}
[XmlText]
public string Value
{
get; set;
}
}
public class ObjSer
{
[XmlElement("Name")]
public NameNode Name
{
get; set;
}
}
To set value of Name:
var obj = new ObjSer();
var valueToSet = "<tag1>Value</tag1>";
//or var valueToSet = "Value";
//With XML tag:
if (valueToSet.Contains("</"))
{
var doc = new XmlDocument();
doc.LoadXml(valueToSet);
obj.Name.tag1 = doc.InnerText;
}
else //Without XML Tags
{
obj.Name.Value = senderRecipient.Name;
}
This solution will work in both cases, but has limitation. It will work only for predefined tags (ex. tag1)

Writing/ Reading Multiple Objects into Xml C#

I am new to Xml and have written the code whcih creates an Xml and Reads it back also.
But i want to have some modifications in the Xml structure.
What i don't want is ArrayOfMovie tag, which is coming as the root tab.
But when i am writing multiple objects into the Xml it shows an ArrayOfMovie tag. As i have to maintain the structure of the class, the upper tag as Movie, then its details and then other movie. Also please if you tell the code to modify the xml, tell the procedure to read the newly structured xml too.
Here is the code for the scenario:
// Movies class which contains the list of Movie objects
public class Movies
{
public List<Movie> movieList = new List<Movie>();
}
//Movie class
public class Movie
{
public string Title
{ get; set; }
public int Rating
{ get; set; }
public DateTime ReleaseDate
{ get; set; }
}
private void CreateXml_Click(object sender, EventArgs e)
{
string filePath = path + textBox_XmlFileName.Text+".xml";
Movie firstMov = new Movie();
firstMov.Title = "Shrek";
firstMov.Rating = 2;
firstMov.ReleaseDate = DateTime.Now;
Movie secondMov = new Movie();
secondMov.Title = "Spider Man";
secondMov.Rating = 4;
secondMov.ReleaseDate = DateTime.Now;
Movies moviesObj = new Movies();
moviesObj.movieList.Add(firstMov);
moviesObj.movieList.Add(secondMov);
List<Movie> movList = new List<Movie>() { firstMov,secondMov};
XmlHandler.SerializeToXml(moviesObj.movieList, filePath);
}
// The static class and funcion that creates the xml file
public static void SerializeToXml(List<Movie> movies ,string filePath)
{
XmlSerializer xls= new XmlSerializer(typeof(List<Movie>));
TextWriter tw = new StreamWriter(filePath);
xls.Serialize(tw, movies);
tw.Close();
}
// It Creates the following output
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfMovie xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Movie>
<Title>Shrek</Title>
<Rating>2</Rating>
<ReleaseDate>2014-05-25T22:55:17.2811063+05:00</ReleaseDate>
</Movie>
<Movie>
<Title>Spider Man</Title>
<Rating>4</Rating>
<ReleaseDate>2014-05-25T22:55:17.2811063+05:00</ReleaseDate>
</Movie>
</ArrayOfMovie>
// The code for reading the file into objects
public static List<Movie> DeserializeFromXml(string filePath)
{
XmlSerializer deserializer = new XmlSerializer(typeof(List<Movie>));
TextReader tr = new StreamReader(#filePath);
List<Movie> movie;
movie = (List<Movie>)deserializer.Deserialize(tr);
tr.Close();
return movie;
}
you may use the XmlRootAttribute if you want to name your root
XmlSerializer deserializer = new XmlSerializer(typeof(List<Movie>),
new XmlRootAttribute("YourRoot"));

How to handle end of file when reading xml file

So I am reading a xml file with unknown length and reading each element into a list structure. Right now once I get to the end of the file I continue reading, this causes an exception. Right now I just catch this exception and continue with my life but is there a cleaner way to do this?
try
{
while(!textReader.EOF)
{
// Used to store info from each command as they are read from the xml file
ATAPassThroughCommands command = new ATAPassThroughCommands ();
// the following is just commands being read and their contents being saved
XmlNodeType node = textReader.NodeType;
textReader.ReadStartElement( "Command" );
node = textReader.NodeType;
name = textReader.ReadElementString( "Name" );
node = textReader.NodeType;
CommandListContext.Add(name);
command.m_Name = name;
command.m_CMD = Convert .ToByte(textReader.ReadElementString("CMD" ),16);
command.m_Feature = Convert .ToByte(textReader.ReadElementString("Feature" ),16);
textReader.ReadEndElement(); //</command>
m_ATACommands.Add(command);
}
}
catch ( Exception ex)
{
//</ATAPassThrough> TODO: this is an ugly fix come up with something better later
textReader.ReadEndElement();
//cUtils.DisplayError(ex.Message);
}
xml file:
<ATAPassThrough>
<Command>
<Name>Smart</Name>
<CMD>B0</CMD>
<Feature>D0</Feature>
</Command>
<Command>
<Name>Identify</Name>
<CMD>B1</CMD>
<Feature>D0</Feature>
</Command>
.
.
.
.
</ATAPassThrough>
I would recomend using XDocument for reading XML data... for instance in your case since you already have a TextReader for your XML you can just pass that into the XDocument.Load method... your entire function above looks like this..
var doc = XDocument.Load(textReader);
foreach (var commandXml in doc.Descendants("Command"))
{
var command = new ATAPassThroughCommands();
var name = commandXml.Descendants("Name").Single().Value;
// I'm not sure what this does but it looks important...
CommandListContext.Add(name);
command.m_Name = name;
command.m_CMD =
Convert.ToByte(commandXml.Descendants("CMD").Single().Value, 16);
command.m_Feature =
Convert.ToByte(commandXml.Descendants("Feature").Single().Value, 16);
m_ATACommands.Add(command);
}
Significantly easier. Let the framework do the heavy lifting for you.
Probably the easiest way if you have normal and consistant XML is to use the XML Serializer.
First Create Objects that match your XML
[Serializable()]
public class Command
{
[System.Xml.Serialization.XmlElement("Name")]
public string Name { get; set; }
[System.Xml.Serialization.XmlElement("CMD")]
public string Cmd { get; set; }
[System.Xml.Serialization.XmlElement("Feature")]
public string Feature { get; set; }
}
[Serializable()]
[System.Xml.Serialization.XmlRoot("ATAPassthrough")]
public class CommandCollection
{
[XmlArrayItem("Command", typeof(Command))]
public Command[] Command { get; set; }
}
The a method to return the CommandCollection
public class CommandSerializer
{
public commands Deserialize(string path)
{
CommandCollection commands = null;
XmlSerializer serializer = new XmlSerializer(typeof(CommandCollection ));
StreamReader reader = new StreamReader(path);
reader.ReadToEnd();
commands = (CommandCollection)serializer.Deserialize(reader);
reader.Close();
return commands ;
}
}
Not sure if this is exactly correct, I don't have the means to test it, but is should be really close.

Categories