Generate XML file and read it back - c#

I'm looking for a good clean way of writing XML to a configuration file and being able to read it back into the Application.
I have an Array of properties that each contain a list of fields.
For example a list of Camera Properties.
Each property has
Name, Value, Category, Type, Description.
I would like my XML Config to look like this
<Camera_Properties>
<Property Name="Height" Value="40" Category="Dimensions" Type="Int" Description="The height of the box">
<Property Name="Width" Value="40" Category="Dimensions" Type="Int" Description="The width of the box">
</Camera_Properties>
Firstly is this possible, or practical?
I Can't seem to find a way of writing this and more impotently being able to parse it in such a way that it can be pulled apart so I could read the Config file and as each element is found call code like this
SetProperty(xmlelement.name, xmlelement.Value, xmlelement.Type);
So far i have managed to get this output
<Camera_Properties>
<Property>
<Name> Height</Name>
<Value> 100 </Value>
<Category> Dimensions </Category>
<Type> Int </Type>
<Description> Height of the box </Description>
</Property>
</Camera_Properties>
But this is not practical for how i need to use the configuration file.
I have used System.Xml.XmlDocument to get this far. If there is a better alternative i would appreciate suggestions.
But as there are hundreds of properties this is too messy and hard to read.
If anyone can help or knows where there may be a relevant example i could use as a guide it would greatly help.
Thanks
EDIT
I would like to loop through and generate an XML file with these properties from an array. The idea is that this file can be used to load in proprieties and set properties as the change each time the hardware is powered off.
public void CreateXML()
{
// Setup document and header tags
foreach(Property prop in propertyArray)
{
create single element here with prop.Name, Prop.Age etc as attributes
}
SaveXml(Filename);
}
The SDK i use for the hardware has an Array of parameters so i would prefer to generate the file from this instead of a dataset.

You can use XmlSerializer to automatically serialize and deserialize your property collection. If you do, you must mark your properties with the [XmlAttribute] attribute to inform the serializer that they are to be serialized as XML attributes.
For instance, given the classes:
public class Property
{
[XmlAttribute]
public string Name { get; set; }
[XmlAttribute]
public string Value { get; set; }
[XmlAttribute]
public string Category { get; set; }
[XmlAttribute]
public string Type { get; set; }
[XmlAttribute]
public string Description { get; set; }
}
[XmlRoot("Camera_Properties")]
public class CameraPropertyList
{
[XmlElement("Property")]
public List<Property> Properties { get; set; }
}
You can test reading and writing them as follows:
string xml = #"<Camera_Properties>
<Property Name=""Height"" Value=""40"" Category=""Dimensions"" Type=""Int"" Description=""The height of the box""/>
<Property Name=""Width"" Value=""40"" Category=""Dimensions"" Type=""Int"" Description=""The width of the box""/>
</Camera_Properties>
";
CameraPropertyList properties;
using (StringReader reader = new StringReader(xml))
{
properties = (CameraPropertyList)(new XmlSerializer(typeof(CameraPropertyList))).Deserialize(reader);
}
string xmlOut;
using (var textWriter = new StringWriter())
{
var settings = new XmlWriterSettings() { Indent = true, IndentChars = " " }; // For cosmetic purposes.
using (var xmlWriter = XmlWriter.Create(textWriter, settings))
(new XmlSerializer(typeof(CameraPropertyList))).Serialize(xmlWriter, properties);
xmlOut = textWriter.ToString();
}
Debug.WriteLine(xmlOut);
The resulting XML output looks like:
<Camera_Properties xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Property Name="Height" Value="40" Category="Dimensions" Type="Int" Description="The height of the box" />
<Property Name="Width" Value="40" Category="Dimensions" Type="Int" Description="The width of the box" />
</Camera_Properties>
If, for some reason, you want to suppress the standard xsi and xsd namespaces, you can do:
using (var textWriter = new StringWriter())
{
var settings = new XmlWriterSettings() { Indent = true, IndentChars = " " }; // For cosmetic purposes.
var ns = new XmlSerializerNamespaces();
ns.Add("", ""); // Disable the xmlns:xsi and xmlns:xsd lines.
using (var xmlWriter = XmlWriter.Create(textWriter, settings))
(new XmlSerializer(typeof(CameraPropertyList))).Serialize(xmlWriter, properties, ns);
xmlOut = textWriter.ToString();
}
Alternatively, if you prefer to do it manually by creating an XmlDocument, you can do:
XmlDocument ToXmlDocument(CameraPropertyList list)
{
var doc = new XmlDocument();
var rootNode = doc.CreateElement("Camera_Properties");
doc.AppendChild(rootNode);
foreach (var property in list.Properties)
{
var element = doc.CreateElement("Property");
element.SetAttribute("Name", property.Name);
element.SetAttribute("Value", property.Value);
element.SetAttribute("Category", property.Category);
element.SetAttribute("Type", property.Type);
element.SetAttribute("Description", property.Description);
rootNode.AppendChild(element);
}
return doc;
}

It looks like you're adding nodes instead of one node with attributes on it.
Try adding one node named Properties, then set the attributes on that node to the key/values you want.
Here's an example:
Microsoft XML Attributes Documentation

I've been doing a lot of work using Serialization at my job.
XmlSerializer
You can create some classes to store the information. Write to the objects properties and then serialize it using the following:
XmlSerializer xsSubmit = new XmlSerializer(typeof(Claim));
var subReq = Claim;
using (StringWriter sww = new Utf8StringWriter())
{
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings
{
Indent = true,
OmitXmlDeclaration = false,
Encoding = Encoding.Unicode
};
using (XmlWriter writer = XmlWriter.Create(sww, xmlWriterSettings))
{
xsSubmit.Serialize(writer, subReq);
var xml = sww.ToString();
PrintOutput(xml);
File.WriteAllText("out.xml", text);
Console.WriteLine(xml);
}
}
This is my working example. Note on the first line I have typeof(Claim) Claim is a parent object of mine which contains my child objects.
Think of each object nested in another object as each nesting of the XML:
public class Claim
{
public Accident Accident { get; set; }
public Driver Driver { get; set; }
public Insurer Insurer { get; set; }
public Owner Owner { get; set; }
public Policy Policy { get; set; }
public Solicitor Solicitor { get; set; }
public Source Source { get; set; }
}
You can see my question last week regarding something similar here: XMLSerialization Nesting
DataSet Serialization
DataSet ds = new Dataset();
DataTable dt = new DataTable();
ds.Tables.Add(dt);
System.IO.StringWriter writer = new System.IO.StringWriter();
Case.WriteXml(writer, XmlWriteMode.WriteSchema);
See here if you're adamant about using attributes: Serialize Property as Xml Attribute in Element
Edit: You can read them back in using both methods. You can read the data back into the objects with XMLSerializer or you can deserialize a dataset.

Related

How to find nodes in an XML

I have loaded the following XML file using xml.Load("myfile.xml"); where xml is of type XmlDocument:
<?xml version="1.0" encoding="ISO-8859-1"?>
<DTE xmlns="http://www.sii.cl/SiiDte" version="1.0">
<Documento ID="E000000005T033F0114525415">
<Encabezado>
<IdDoc>
<TipoDTE>33</TipoDTE>
<Folio>114525415</Folio>
<FchEmis>2021-11-02</FchEmis>
<FmaPago>1</FmaPago>
<FchVenc>2021-11-02</FchVenc>
</IdDoc>
</Encabezado>
</Documento>
</DTE>
How can I get Folionode?
I have tried with:
xml.DocumentElement.SelectSingleNode("/DTE/Documento/Encabezado/IdDoc/Folio");
xml.DocumentElement.SelectNodes("DTE/Documento/Encabezado/IdDoc/Folio")
xml.DocumentElement.SelectSingleNode("//DTE/Documento/Encabezado/IdDoc/Folio");
xml.DocumentElement.SelectSingleNode("/Documento/Encabezado/IdDoc/Folio");
xml.DocumentElement.SelectSingleNode("Documento/Encabezado/IdDoc/Folio");
xml.DocumentElement.SelectSingleNode("/Encabezado/IdDoc/Folio");
xml.DocumentElement.SelectNodes("/DTE/Documento/Encabezado/IdDoc/Folio")
when I debug xml.DocumentElement I see that the element is DTE so I think xml.DocumentElement.SelectSingleNode("Documento/Encabezado/IdDoc/Folio") should do it.
When I get xml.DocumentElement.FirstChild I get Documento node.
With xml.DocumentElement.FirstChild.FirstChild I get Encabezado node.
With xml.DocumentElement.FirstChild.FirstChild.FirstChild I get IdDoc node.
If I use xml.DocumentElement.FirstChild.FirstChild.FirstChild.SelectSingleNode("Folio"), returned value is null.
If I use xml.DocumentElement.FirstChild.FirstChild.FirstChild.ChildNodes, I get the 5 elements.
Then I could use xml.DocumentElement.FirstChild.FirstChild.FirstChild.ChildNodes[1].InnerText to get Folio value.
I can traverse the XML but, how can I do it to get the element directly?
Thanks
Jaime
It is better to use LINQ to XML API for your task. It is available in the .Net Framework since 2007.
The provided XML has a default namespace. It needs to be declared and used, otherwise it is imposable to find any XML element.
c#
void Main()
{
const string filename = #"e:\Temp\jstuardo.xml";
XDocument xdoc = XDocument.Load(filename);
XNamespace ns = xdoc.Root.GetDefaultNamespace();
string Folio = xdoc.Descendants(ns + "Folio")
.FirstOrDefault()?.Value;
Console.WriteLine("Folio='{0}'", Folio);
}
Output
Folio='114525415'
You can try to use the Xpath like below:
XmlDocument doc = new XmlDocument();
doc.Load("myfile.xml");
var node= doc.SelectSingleNode("Documento/Encabezado/IdDoc/[Folio='"114525415"']");
There are few ways to make things up with your issue.
So, we have our XML:
const string MyXML = #"<?xml version=""1.0"" encoding=""ISO-8859-1""?>
<DTE xmlns=""http://www.sii.cl/SiiDte"" version=""1.0"">
<Documento ID=""E000000005T033F0114525415"">
<Encabezado>
<IdDoc>
<TipoDTE>33</TipoDTE>
<Folio>114525415</Folio>
<FchEmis>2021-11-02</FchEmis>
<FmaPago>1</FmaPago>
<FchVenc>2021-11-02</FchVenc>
</IdDoc>
</Encabezado>
</Documento>
</DTE>";
And we need to get Folio node (exactly node, not just value). We can use:
XmlNamespaceManager:
to find descendant node(s) through XML namespace (xmlns) alias in XPath:
// Creating our XmlDocument instance
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(MyXML);
// Initializing XmlNamespaceManager and providing our xmlns with 'SiiDte' alias:
var xmlNamespaceManager = new XmlNamespaceManager(xmlDocument.NameTable);
xmlNamespaceManager.AddNamespace("SiiDte", "http://www.sii.cl/SiiDte");
// Declaring our simple shiny XPath:
var xPath = "descendant::SiiDte:Folio";
// If we need single (first) element:
var folio = xmlDocument.DocumentElement.SelectSingleNode(xPath, xmlNamespaceManager);
// If we need all Folios:
var folios = xmlDocument.DocumentElement.SelectNodes(xPath, xmlNamespaceManager);
XDocument and its Descendants:
from System.Xml.Linq namespace and its XDocument class, to find descendant node(s) just by their tag name <Folio>:
// If we need single (first) element:
var folio = XDocument.Parse(MyXML)
.Descendants()
.FirstOrDefault(x => x.Name.LocalName == "Folio");
// Add System.Linq using to access FirstOrDefault extension method
// If we need all Folios - just replacing FirstOrDefault with Where extension method:
var folios = XDocument.Parse(MyXML)
.Descendants()
.Where(x => x.Name.LocalName == "Folio"); // and .ToList() if you need
// Or we can use also our XML namespace to filter Descendants:
var ns = (XNamespace)"http://www.sii.cl/SiiDte";
var folios = XDocument.Parse(MyXML).Descendants(ns + "Folio");
Deserialization:
to operate not with XML or nodes, but with some class (e.g. DTE), which represents your XML schema. I'm not sure that I totally understand your XML structure, but anyway as example it could be used.
So we create our classes, which are representation of our XML:
[Serializable, XmlRoot(ElementName = nameof(DTE), Namespace = "http://www.sii.cl/SiiDte")]
public class DTE
{
[XmlAttribute("version")]
public string Version { get; set; }
[XmlElement(nameof(Documento))]
public List<Documento> Documentacion { get; set; }
}
[Serializable]
public class Documento
{
[XmlAttribute(nameof(ID))]
public string ID { get; set; }
[XmlElement(nameof(Encabezado))]
public Encabezado Encabezado { get; set; }
}
[Serializable]
public class Encabezado
{
[XmlElement(nameof(IdDoc))]
public IDDoc IdDoc { get; set; }
}
[Serializable]
public class IDDoc
{
[XmlElement(nameof(TipoDTE))]
public int TipoDTE { get; set; }
[XmlElement(nameof(Folio))]
public long Folio { get; set; }
[XmlElement(nameof(FchEmis))]
public DateTime FchEmis { get; set; }
[XmlElement(nameof(FmaPago))]
public int FmaPago { get; set; }
[XmlElement(nameof(FchVenc))]
public DateTime FchVenc { get; set; }
}
Now we can easily create our DTE object with XmlSerializer class and its Deserialize method:
// Declaring our DTE object
var dte = (DTE)null;​
using (var reader = new StringReader(MyXML))
{
dte = (DTE)new XmlSerializer(typeof(DTE)).Deserialize(reader);
}
Now we can get Folio as property of IdDoc class, which is property of Encabezado class, which in a turn is property of Documento class. Keeping in mind possible null result turns us to use, for example, null-propagation:
var folio = dte?.Documentacion.FirstOrDefault()?.Encabezado?.IdDoc?.Folio;
As Documentacion is a List<Documento> - we use again FirstOrDefault (also may be used ElementAtOrDefault(0)) to "imitate" SelectSingleNode. And for all Folios we can use Select (also with mull-propagation):
var folios = dte?.Documentacion.Select(x => x?.Encabezado?.IdDoc?.Folio);
Sure, we can edit properties if we want or add new:
// Edit
if (dte?.Documentacion.FirstOrDefault() is Documento documento)
documento.Encabezado.IdDoc.Folio = 112233445566;
// or create and add new
var newDocumento = new Documento
{
ID = "SomeID",
Encabezado = new Encabezado
{
IdDoc = new IDDoc
{
TipoDTE = 123,
Folio = 112233445566,
FmaPago = 1,
FchEmis = DateTime.Now,
FchVenc = DateTime.Now.AddDays(1)
}
}
};
dte.Documentacion.Add(newDocumento);
And finally save back to XML file using Serialization. Here became usable our class and properties attributes (e.g. [Serializable], [XmlElement] etc), which specifies how each property should be named or represented in XML:
using (var xmlWriter = XmlWriter.Create("My.xml",
new XmlWriterSettings
{
Encoding = Encoding.GetEncoding("ISO-8859-1"),
Indent = true
}))
{
// We remove default XSI, XSD namespaces and leave only our custom:
var xmlSerializerNamespaces = new XmlSerializerNamespaces();
xmlSerializerNamespaces.Add("", "http://www.sii.cl/SiiDte");
// and saving our DTE object to XML file.
xmlSerializer.Serialize(xmlWriter, dte, xmlSerializerNamespaces);
}
Remarks
Of course, parse of XML strings could be replaces with loading XML files (by FileStreams) if needed. And of course you can edit DTE (and child) classes with other properties and mark them as XML attributes or XML elements or making collections with XmlArray and XmlArrayItem attributes - whatever, depending on your needs. Also notice about null XML nodes or its values and take care about it with, for example, Nullable<T> (e.g. long?, DateTime?), IsNullable property of XML attributes and some kind of value validation at property setter:
private long _folio;
[XmlElement(nameof(Folio), IsNullable = true)]
public long? Folio
{
get => _folio;
set => _folio = value ?? 0L; // Null-coalescing with default fallback value of 0L
}
Hope it would be helpful for your future purposes.

How to parse xml with multiple xmlns attribute in c#?

I have an xml beginning like above
<Invoice
xsi:schemaLocation="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2../xsdrt/maindoc/UBL-Invoice-2.1.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xades="http://uri.etsi.org/01903/v1.3.2#"
xmlns:ext="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
<cbc:UBLVersionID>2.1</cbc:UBLVersionID>
<cbc:CustomizationID>TR1.2</cbc:CustomizationID>
<cbc:ProfileID>TEMELFATURA</cbc:ProfileID>
<cbc:ID>ALP2018000007216</cbc:ID>
<!-- ... -->
and I try to parse the xml with method like that
public static T FromXml<T>(string xmlString)
{
StringReader xmlReader = new StringReader(xmlString);
XmlSerializer deserializer = new XmlSerializer(typeof(T));
return (T)deserializer.Deserialize(xmlReader);
}
and my xml model is like above
[Serializable]
[XmlRoot(
Namespace = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2",
ElementName = "Invoice",
DataType = "string",
IsNullable = true)]
public class Invoice
{
public string CustomizationID { get; set; }
// ...
}
However, I cannot parse the xml document, all values come null. I think that it is because of multiple xmlns attribute in Invoice tag. I couldnt solve the problem.
The default namespace of the document is urn:oasis:names:specification:ubl:schema:xsd:Invoice-2 which you have correctly put in the XmlRoot, but the child elements such as UBLVersionID are prefixed with cbc, which is a different namespace. You have to put that namespace against the property to let the serializer know that's what it is.
For example:
[Serializable]
[XmlRoot(
Namespace = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2",
ElementName = "Invoice",
DataType = "string",
IsNullable = true)]
public class Invoice
{
[XmlElement(Namespace = "urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2")]
public string CustomizationID { get; set; }
// ...
}
In Visual Studio, you can use Edit > Paste Special > Paste Xml As Classes to see how to decorate a class to match your XML if you're in doubt.

How to remove empty namespace attribute on manually added xml string when serializing object?

I am using XmlSerializer to output my object model to XML. Everything works very well but now I need to add several lines of pre-built XML to the object without building classes for each line. After lots of searching, I found that I can convert the xml string to an XmlElement using XmlDocument's LoadXml and DocumentElement calls. I get the XML I want except that the string section has an empty namespace. How can I eliminate the empty namespace attribute? Is there a better way to add an xml string to the object and have it be serialized properly?
Note: I am only creating output so I don't need to deserialize the generated XML. I am fairly new to the C#, .NET world, and hence, XmlSerialize.
Here is my code:
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
public XmlElement Extension { get; set; }
public Book()
{
}
public void AddExtension()
{
string xmlString = "<AdditionalInfo>" +
"<SpecialHandling>Some Value</SpecialHandling>" +
"</AdditionalInfo>";
this.Extension = GetElement(xmlString);
}
public static XmlElement GetElement(string xml)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
return doc.DocumentElement;
}
}
static void Main(string[] args)
{
TestSerialization p = new TestSerialization();
Book bookOne = new Book();
bookOne.Title = "How to Fix Code";
bookOne.Author = "Dee Bugger";
bookOne.AddExtension();
System.Xml.Serialization.XmlSerializer serializer = new XmlSerializer(typeof(Book), "http://www.somenamespace.com");
using (var writer = new StreamWriter("C:\\BookReport.xml"))
using (var xmlWriter = XmlWriter.Create(writer, new XmlWriterSettings { Indent = true }))
{
serializer.Serialize(xmlWriter, bookOne);
}
}
Here is my output:
<?xml version="1.0" encoding="utf-8"?>
<Book xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.somenamespace.com">
<Title>How to Fix Code</Title>
<Author>Dee Bugger</Author>
<Extension>
<AdditionalInfo xmlns="">
<SpecialHandling>Some Value</SpecialHandling>
</AdditionalInfo>
</Extension>
</Book>
It is the xmlns="" on AdditionalInfo that I want to eliminate. I believe this coming out because there is no association between the XmlDocument I created and the root serialized object, so the XmlDocument creates its own namespace. How can I tell the XmlDocument (and really, the generated XmlElement) that it belongs to the same namespace as the serialized object?
This is added because the parent elements have a namespace and your AdditionalInfo element does not. The xmlns="" attribute changes the default namespace for that element and its children.
If you want to get rid of it, then presumably you want the AdditionalInfo element to have the same namespace as its parent. In which case, you need to change your XML to this:
string xmlString = #"<AdditionalInfo xmlns=\"http://www.somenamespace.com\">" +
"<SpecialHandling>Some Value</SpecialHandling>" +
"</AdditionalInfo>";

Using the .NET serializer to serialize XML to a .NET class

I have an XML file:
<?xml version="1.0" encoding="UTF-8"?>
<MyProducts>
<Product Name="P1" />
<Product Name="P2" />
</MyProducts>
And a C# object:
Public Class Product
{
[XmlAttribute]
Public Name {get; set;}
}
Using the .NET Serializer class now can I Deserialize the XML file into an IList without creating a MyProducts object?
So I want to somehow select only the Product elements before I serialize
There's a quick-and-dirty way to accomplish what you want - simply replace "MyProducts" with something the BCL classes are happy with - ArrayOfProduct:
string xml = #"<?xml version='1.0' encoding='UTF-8;'?>
<MyProducts>
<Product Name='P1' />
<Product Name='P2' />
</MyProducts>";
//secret sauce:
xml = xml.Replace("MyProducts>", "ArrayOfProduct>");
IList myProducts;
using (StringReader sr = new StringReader(xml))
{
XmlSerializer xs = new XmlSerializer(typeof(List<Product>));
myProducts = xs.Deserialize(sr) as IList;
}
foreach (Product myProduct in myProducts)
{
Console.Write(myProduct.Name);
}
Of course, the right way would be to transform the XML document to replace the MyProducts nodes appropriately - instead of using a string replace - but this illustrates the point.
If you don't want to create a collection class for your products, you can mix some LINQ to XML with the XmlSerializer:
public static IEnumerable<T> DeserializeMany<T>(
string fileName, string descendentNodeName = null) {
descendentNodeName = descendentNodeName ?? typeof(T).Name;
var serializer = new XmlSerializer(typeof(T));
return
from item in XDocument.Load(fileName).Descendants(descendentNodeName)
select (T)serializer.Deserialize(item.CreateReader());
}
Then, to get your list:
var products = XmlSerializerUtils.DeserializeMany<Product>(fileName).ToList();
I'm not sure you're going to have much success using the XML serializer to accomplish what you need. It may be simpler for you to manually parse out the XML and map them explicitly, e.g.
XDocument xml = XDocument.Parse(#"<?xml version=""1.0"" encoding=""UTF-8""?>
<MyProducts>
<Product Name=""P1"" />
<Product Name=""P2"" />
</MyProducts>");
foreach(var product in xml.Descendants(XName.Get("Product")))
{
var p = new Product { Name = product.Attribute(XName.Get("Name")).Value };
// Manipulate your result and add to your collection.
}
...
public class Product
{
public string Name { get; set; }
}
If you're using a file, which you most likely are for your XML, just replace the Parse method on the XDocument w/ Load and the appropriate signature.
I don't think you can instruct the serializer to spit out a IList. The serializer can create a MyProduct collection object and fill it with Products. Which sounds like what you want to do.
You can also use LINQ to query the XML document and create a list of IEnumerable as well.
// load from stream if that is the case
// this just uses a file for demonstration purposes
XDocument doc = XDocument.Load("location_of_source.xml");
// select all Product nodes from the root node and create a new Product object using
// object initialization syntax
var listOfProduct = doc.Descendants("Product")
.Select(p => new Product { Name = p.Attribute("Name").Value});
While it's novel to not create the class, doing so saves you a lot of code... You don't even have to use it except when you deserialize.
//Products declaration
[XmlRoot(ElementName = "MyProducts")]
public class MyProducts : List<Product>
{
}
public class Product
{
[XmlAttribute]
public string Name { get; set; }
}
...
[Test]
public void DeserializeFromString()
{
var xml = #"<?xml version='1.0' encoding='UTF-8;'?>
<MyProducts>
<Product Name='P1' />
<Product Name='P2' />
</MyProducts>";
IList<Product> obj = Serialization<MyProducts>.DeserializeFromString(xml);
Assert.IsNotNull(obj);
Assert.AreEqual(2, obj.Count);
}
...
//Deserialization library
public static T DeserializeFromString(string serialized)
{
var byteArray = Encoding.ASCII.GetBytes(serialized);
var memStream = new MemoryStream(byteArray);
return Deserialize(memStream);
}
public static T Deserialize(Stream stream)
{
var xs = new XmlSerializer(typeof(T));
return (T) xs.Deserialize(stream);
}
The problem is that IList is not serializable so you'd have to implement your custom XmlSerializer - walk through xml nodes etc, you can however deserialize your collection into List using out of the box XmlSerializer.
Dont forget to add default constructor to your Product Class.
XmlSerializer serializer = new XmlSerializer(typeof(List<Product>));
FileStream stream = new FileStream(fileName, FileMode.Open);
var product = serializer.Deserialize(sream) as List<Product>;

Adding attributes to an XML node

How can I create an xml file dynamically, with the following structure?
<Login>
<id userName="Tushar" passWord="Tushar">
<Name>Tushar</Name>
<Age>24</Age>
</id>
</Login>
I am not able to create the attributes inside the id tag (i.e. userName="" and passWord="").
I am using C# in a windows application.
Some Important namespace that you might require is
using System.Xml;
using System.IO;
Well id isn't really the root node: Login is.
It should just be a case of specifying the attributes (not tags, btw) using XmlElement.SetAttribute. You haven't specified how you're creating the file though - whether you're using XmlWriter, the DOM, or any other XML API.
If you could give an example of the code you've got which isn't working, that would help a lot. In the meantime, here's some code which creates the file you described:
using System;
using System.Xml;
class Test
{
static void Main()
{
XmlDocument doc = new XmlDocument();
XmlElement root = doc.CreateElement("Login");
XmlElement id = doc.CreateElement("id");
id.SetAttribute("userName", "Tushar");
id.SetAttribute("passWord", "Tushar");
XmlElement name = doc.CreateElement("Name");
name.InnerText = "Tushar";
XmlElement age = doc.CreateElement("Age");
age.InnerText = "24";
id.AppendChild(name);
id.AppendChild(age);
root.AppendChild(id);
doc.AppendChild(root);
doc.Save("test.xml");
}
}
There is also a way to add an attribute to an XmlNode object, that can be useful in some cases.
I found this other method on msdn.microsoft.com.
using System.Xml;
[...]
//Assuming you have an XmlNode called node
XmlNode node;
[...]
//Get the document object
XmlDocument doc = node.OwnerDocument;
//Create a new attribute
XmlAttribute attr = doc.CreateAttribute("attributeName");
attr.Value = "valueOfTheAttribute";
//Add the attribute to the node
node.Attributes.SetNamedItem(attr);
[...]
The latest and supposedly greatest way to construct the XML is by using LINQ to XML:
using System.Xml.Linq
var xmlNode =
new XElement("Login",
new XElement("id",
new XAttribute("userName", "Tushar"),
new XAttribute("password", "Tushar"),
new XElement("Name", "Tushar"),
new XElement("Age", "24")
)
);
xmlNode.Save("Tushar.xml");
Supposedly this way of coding should be easier, as the code closely resembles the output (which Jon's example above does not). However, I found that while coding this relatively easy example I was prone to lose my way between the cartload of comma's that you have to navigate among. Visual studio's auto spacing of code does not help either.
You can use the Class XmlAttribute.
Eg:
XmlAttribute attr = xmlDoc.CreateAttribute("userName");
attr.Value = "Tushar";
node.Attributes.Append(attr);
If you serialize the object that you have, you can do something like this by using "System.Xml.Serialization.XmlAttributeAttribute" on every property that you want to be specified as an attribute in your model, which in my opinion is a lot easier:
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public class UserNode
{
[System.Xml.Serialization.XmlAttributeAttribute()]
public string userName { get; set; }
[System.Xml.Serialization.XmlAttributeAttribute()]
public string passWord { get; set; }
public int Age { get; set; }
public string Name { get; set; }
}
public class LoginNode
{
public UserNode id { get; set; }
}
Then you just serialize to XML an instance of LoginNode called "Login", and that's it!
Here you have a few examples to serialize and object to XML, but I would suggest to create an extension method in order to be reusable for other objects.

Categories