Using the .NET serializer to serialize XML to a .NET class - c#

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>;

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 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>";

Reading large XML files with C#

I would like to know how can I read a XML file from my desktop and put it into a string?
Here is my XML:
<smallusers>
<user id="1">
<name>John</name>
<motto>I am john, who are you?</motto>
</user>
<user id="2">
<name>Peter</name>
<motto>Hello everyone!</motto>
</user>
</smallusers>
<bigusers>
<user id="3">
<name>Barry</name>
<motto>Earth is awesome</motto>
</user>
</bigusers>
I want to store each user, but still detect if their small or big, is there a way to do this?
Before you downrate this, you might want to check google because I did research, but found nothing.
"Before you downrate this, you might want to check google because I
did research, but found nothing"
You found nothing because you don't know what you are searching for, also your XML is invalid, you need to enclose it in a rootElement. Then the first thing you need to do is read this file from the desktop if it exists.
You can check the size if you wish at that time and determine if this is "too large" even though it doesn't really matter. I highly doubt your XML file will be 5+ GB in size. If it is then you need an alternative, no single object in a .Net program may be over 2GB, the best you could do is 1,073,741,823 on a 64bit machine.
For very large XML files, anything above 1.0 GB, combine XmlReader and LINQ as stated by Jon Skeet here:
If your document is particularly huge, you can combine XmlReader and
LINQ to XML by creating an XElement from an XmlReader for each of your
"outer" elements in a streaming manner: this lets you do most of the
conversion work in LINQ to XML, but still only need a small portion of
the document in memory at any one time.
For small XML files, anything 1.0 GB or lower stick to the DOM as shown below.
With that said, what you need is to learn what Serialization and Deserialization mean.
Serialize convert an object instance to an XML document.
Deserialize convert an XML document into an object instance.
Instead of XML you can also use JSON, binary, etc.
In your case this is what can be done to Deserialize this XML document back into an Object in order for you to use in your code.
First fix up the XML and give it a Root.
<?xml version="1.0" encoding="UTF-8"?>
<DataRoot>
<smallusers>
<user id="1">
<name>John</name>
<motto>I am john, who are you?</motto>
</user>
<user id="2">
<name>Peter</name>
<motto>Hello everyone!</motto>
</user>
</smallusers>
<bigusers>
<user id="3">
<name>Barry</name>
<motto>Earth is awesome</motto>
</user>
</bigusers>
</DataRoot>
Then create the root class in C#, you may generate this directly in Visual Studio 2012+ by copying your XML and going to Edit - Paste Special, but I like to use: XML to C# Class Generator
Here is what your code would look like after you generate the C# Root Class for your XML, hope it helps you understand it better.
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
public class Program
{
[XmlRoot(ElementName = "user")]
public class User
{
[XmlElement(ElementName = "name")]
public string Name { get; set; }
[XmlElement(ElementName = "motto")]
public string Motto { get; set; }
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
}
[XmlRoot(ElementName = "smallusers")]
public class Smallusers
{
[XmlElement(ElementName = "user")]
public List<User> User { get; set; }
}
[XmlRoot(ElementName = "bigusers")]
public class Bigusers
{
[XmlElement(ElementName = "user")]
public User User { get; set; }
}
[XmlRoot(ElementName = "DataRoot")]
public class DataRoot
{
[XmlElement(ElementName = "smallusers")]
public Smallusers Smallusers { get; set; }
[XmlElement(ElementName = "bigusers")]
public Bigusers Bigusers { get; set; }
}
static void Main(string[] args)
{
string testXMLData = #"<DataRoot><smallusers><user id=""1""><name>John</name><motto>I am john, who are you?</motto></user><user id=""2""><name>Peter</name><motto>Hello everyone!</motto></user></smallusers><bigusers><user id=""3""><name>Barry</name><motto>Earth is awesome</motto></user></bigusers></DataRoot>";
var fileXmlData = File.ReadAllText(#"C:\XMLFile.xml");
var deserializedObject = DeserializeFromXML(fileXmlData);
var serializedToXML = SerializeToXml(deserializedObject);
//I want to store each user, but still detect if their small or big, is there a way to do this?
foreach (var smallUser in deserializedObject.Smallusers.User)
{
//Iterating your collection of Small users?
//Do what you need here with `smalluser`.
var name = smallUser.Name; //Example...
}
Console.WriteLine(serializedToXML);
Console.ReadKey();
}
public static string SerializeToXml(DataRoot DataObject)
{
var xsSubmit = new XmlSerializer(typeof(DataRoot));
using (var sw = new StringWriter())
{
using (var writer = XmlWriter.Create(sw))
{
xsSubmit.Serialize(writer, DataObject);
var data = sw.ToString();
writer.Flush();
writer.Close();
sw.Flush();
sw.Close();
return data;
}
}
}
public static DataRoot DeserializeFromXML(string xml)
{
var xsExpirations = new XmlSerializer(typeof(DataRoot));
DataRoot rootDataObj = null;
using (TextReader reader = new StringReader(xml))
{
rootDataObj = (DataRoot)xsExpirations.Deserialize(reader);
reader.Close();
}
return rootDataObj;
}
}
}

Generate XML file and read it back

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.

XML Serializing Element with prefix

<?xml version="1.0" encoding="UTF-8"?>
<rs:model-request xsi:schemaLocation="http://www.ca.com/spectrum/restful/schema/request ../../../xsd/Request.xsd " xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rs="http://www.ca.com/spectrum/restful/schema/request" throttlesize="100">
<rs:target-models>
I am having trouble understanding the C# XmlSerializer. I have successfully been able to serialize elements that do not have a prefix such as rs:* above. I also have no been able to find how to add the xsi:, xmlns:xsi and xmlns:rs (namespaces?).
Would someone be able to create a simple class to show how to generate the above XML?
Fields, properties, and objects can have a namespace associated with them for serialization purposes. You specify the namespaces using attributes such as [XmlRoot(...)], [XmlElement(...)], and [XmlAttribute(...)]:
[XmlRoot(ElementName = "MyRoot", Namespace = MyElement.ElementNamespace)]
public class MyElement
{
public const string ElementNamespace = "http://www.mynamespace.com";
public const string SchemaInstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance";
[XmlAttribute("schemaLocation", Namespace = SchemaInstanceNamespace)]
public string SchemaLocation = "http://www.mynamespace.com/schema.xsd";
public string Content { get; set; }
}
Then you associate the desired namespace prefixes during serialization by using the XmlSerializerNamespaces object:
var obj = new MyElement() { Content = "testing" };
var namespaces = new XmlSerializerNamespaces();
namespaces.Add("xsi", MyElement.SchemaInstanceNamespace);
namespaces.Add("myns", MyElement.ElementNamespace);
var serializer = new XmlSerializer(typeof(MyElement));
using (var writer = File.CreateText("serialized.xml"))
{
serializer.Serialize(writer, obj, namespaces);
}
The final output file looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<myns:MyRoot xmlns:myns="http://www.mynamespace.com" xsi:schemaLocation="http://www.mynamespace.com/schema.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<myns:Content>testing</myns:Content>
</myns:MyRoot>

Categories