How can I conditionally write out XML in a less verbose way? - c#

I want to write a xml file with C#. It is a basic file like that :
<EmployeeConfiguration>
<Bosses>
<Boss name="BOB">
<Employees>
<Employee Id="#0001" />
<Employee Id="#0002" />
</Employees>
<Boss>
</Bosses>
</EmployeeConfiguration>
and I don't want have an Employees node if there is not Employee node...
I want use XElement but I can't because of that... So I used XmlWriter. it works fine but I find it's very verbose to write XML :
EmployeeConfiguration config = EmployeeConfiguration.GetConfiguration();
using (XmlWriter writer = XmlWriter.Create(_fileUri, settings))
{
writer.WriteStartDocument();
// <EmployeeConfiguration>
writer.WriteStartElement("EmployeeConfiguration");
if (config.Bosses.Count > 0)
{
// <Bosses>
writer.WriteStartElement("Bosses");
foreach (Boss b in config.Bosses)
{
// Boss
writer.WriteStartElement("Boss");
writer.WriteStartAttribute("name");
writer.WriteString(b.Name);
writer.WriteEndAttribute();
if (b.Employees.Count > 0)
{
writer.WriteStartElement("Employees");
foreach (Employee emp in b.Employees)
{
writer.WriteStartElement(Employee);
writer.WriteStartAttribute(Id);
writer.WriteString(emp.Id);
writer.WriteEndAttribute();
writer.WriteEndElement();
}
}
}
}
}
Is there another (and fastest) way to write this kind of xml file ?

If you mean "fastest" as the fastest way to write some code to do it (and the simplest), then creating a custom class and serializing it using the XmlSerializer is the way to go...
Create your classes as follows:
[XmlRoot("EmployeeConfiguration")]
public class EmployeeConfiguration
{
[XmlArray("Bosses")]
[XmlArrayItem("Boss")]
public List<Boss> Bosses { get; set; }
}
public class Boss
{
[XmlAttribute("name")]
public string Name { get; set; }
[XmlArray("Employees")]
[XmlArrayItem("Employee")]
public List<Employee> Employees { get; set; }
}
public class Employee
{
[XmlAttribute]
public string Id { get; set; }
}
and then you can serialize these out with this:
// create a serializer for the root type above
var serializer = new XmlSerializer(typeof (EmployeeConfiguration));
// by default, the serializer will write out the "xsi" and "xsd" namespaces to any output.
// you don't want these, so this will inhibit it.
var namespaces = new XmlSerializerNamespaces(new [] { new XmlQualifiedName("", "") });
// serialize to stream or writer
serializer.Serialize(outputStreamOrWriter, config, namespaces);
As you can see - using various attributes on the classes instruct the serializer in how it should serialize the class. Some of the ones I've included above are actually the default settings and don't explicitly need to be stated - but I've included them to show you how it is done.

You might want to look at XML serialization, using the XmlElement, XmlAttribute (and so on) attributes. I think this gives you the level of control you want, and a very quick, safe and easy to maintain call to do the XML conversion.

var xml = new XElement("EmployeeConfiguration",
new XElement("Bosses"),
new XElement("Boss", new XAttribute("name", "BOB"),
new XElement("Employees"),
new XElement("Employee", new XAttribute("Id", "#0001")),
new XElement("Employee", new XAttribute("Id", "#0001"))
)
);

Related

How to serialize a class instance to XML in .NET Core?

I am trying to serialize a class instance of the following class into a XML string.
[Serializable]
[XmlRoot]
public class Orders
{
[XmlElement("Order")]
public Order order { get; set; }
}
The serializations works fine, but I want to remove the two defaults attributes from the Orders element. Although I need another encoding (ISO-8859-1) and I have to add other attributes to the Orders element.
I wanted to solve the latter by adding a [XmlAttribute] to the Orders class, but it results in the same output.
<?xml version="1.0" encoding="utf-16"?>
<Orders xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Order>
...
<Order>
</Orders>
Does anybody know, how I could achieve that? I highly appreciate any kind of help, cheers! (:
My current root class, which should have a root attribute
public class AGTOSV
{
private string _attribute = "Hello World";
[XmlAttribute]
public string xmlns
{
get { return _attribute; }
set { _attribute = value; }
}
// ....
If I understood you correctly, you just want to export plain xml.
Then you need to set XmlWriterSettings as below and tell the serializer to use a empty namespace.
var emptyNamespaces = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
var settings = new XmlWriterSettings()
{
OmitXmlDeclaration = true,
};
using (var stream = new StringWriter())
using (var writer = XmlWriter.Create(stream, settings))
{
var serializer = new XmlSerializer(orders.GetType());
serializer.Serialize(writer, orders, emptyNamespaces);
string xml = stream.ToString();
}
Also see How can I make the xmlserializer only serialize plain xml?.

How to deserialize two different XML types in to one class

I have two different XML documents. Their structure is almost identical, but they have some different elements.
I would like to deserialize the incoming documents in to one class which is a superset of both classes. There is no need to ever serialize the class, I only need to deserialize the documents.
The XML document types have a different root element, lets say the root of the first is <CLASSA>and the other is <CLASSB>.
I am looking for something like this, where both <CLASSA> and <CLASSB> xml documents are mapped to ClassAandB:
[XmlRoot(ElementName="CLASSA,CLASSB")]
public class ClassAandB {
[XmlElement(ElementName="syntaxid")]
public Syntaxid Syntaxid{ get; set; }
[XmlElement(ElementName="email")]
public Email Email { get; set; }
[XmlElement(ElementName="envelope")]
public Envelope Envelope { get; set; }
[XmlElement(ElementName="header")]
public Header Header { get; set; }
}
I can then find out which of the two types it is by reading the Syntaxid property. This helps me because a lot of the processing is the same for both types.
Any suggestions how to do this?
Since the xml root element name might depend on the content of the xml document, you'll have to configure the XmlSerializer at runtime with this xml root element name to be used.
In this case, there's no need anymore to apply an XmlRootAttribute.
This can be done via the constructor overload accepting an XmlRootAttribute argument, via which you pass the root element name.
public XmlSerializer (Type type, System.Xml.Serialization.XmlRootAttribute root);
You might know the root element name in front eg. depending on the source of the xml document, or you might discover it at runtime from the xml document itself.
The following from the example below shows how the xml root element name gets set.
String rootName = "CLASSA"; // "CLASSB"
var serializer = new XmlSerializer(typeof(ClassAandB), new XmlRootAttribute(rootName));
An simplified example using an XmlReader as source and retrieving the root xml element name from the content.
public class ClassAandB
{
[XmlElement(ElementName="syntaxid")]
public String Syntaxid{ get; set; }
[XmlElement(ElementName="email")]
public String Email { get; set; }
[XmlElement(ElementName="header")]
public String Header { get; set; }
}
var classA = Deserialize(XmlReader.Create(
new StringReader("<CLASSA><syntaxid>A</syntaxid></CLASSA>"))
);
Console.WriteLine(classA.Syntaxid); // A
var classB = Deserialize(
XmlReader.Create(new StringReader("<CLASSB><syntaxid>B</syntaxid></CLASSB>"))
);
Console.WriteLine(classB.Syntaxid); // B
public static ClassAandB Deserialize(XmlReader reader)
{
reader.MoveToContent();
string rootName = reader.Name;
var serializer = new XmlSerializer(typeof(ClassAandB),
new XmlRootAttribute(rootName)
);
var deserialized = serializer.Deserialize(reader) as ClassAandB;
return deserialized;
}
I suggest you to remove XmlRoot attribute and use:
var doc = new XmlDocument();
doc.Load("file.xml");
XmlElement root = xmlDoc.DocumentElement;
var serializer = new XmlSerializer(typeof(ClassAandB), new XmlRootAttribute(root.ToString()));

<xmlns='' > was not expected. But cannot define XMLroot as it changes. C#

I have a site that imports bookings from an external XML file and stores them as nodes in an Umbraco site.
The issue:
Basically, The system in which I exported the bookings from has changed how it exports the XML and the original root node.
I am assuming this is an issue related to this root node as many answers on stack have mentioned this in regards to the error in the subject title, but none that I could see have covered how to deal with a dynamic rootnode.
What I used to have in the XML file:
<Bookings>
<row id="1">
<FirstBookingAttribute>....
</row>
</Bookings>
Now is dynamic and can end up looking like:
<BookingsFebruary2017 company="CompanyNameHere">
<row id="1">
<firstBookingAttribute>....
</row>
</BookingsFebruary2017>
Because of this I am not entirely sure how to set the XMLroot node in C#.
Incase this issue is not related to the root node, I have pasted the erroring code below:
Controller File
var reader = new StreamReader(tempFile);
var deserializer = new XmlSerializer(typeof(Bookings));
var xml = deserializer.Deserialize(reader);
var studentBookings = (Bookings) xml;
Model of Bookings
public class Bookings
{
[XmlElement("row")]
public List<Booking> StudentBookings { get; set; }
}
Thank you
XmlSerializer doesn't allow for dynamic root names out of the box. The expected root name is fixed, and is defined by the type name, the presence of an [XmlRoot] or [XmlType] attribute, and possibly the XmlSerializer constructor used.
Thus an easy solution is to pre-load the XML into an XDocument, modify the root name, and then deserialize:
XDocument doc;
using (var xmlReader = XmlReader.Create(tempFile))
{
doc = XDocument.Load(xmlReader);
}
var rootName = doc.Root.Name;
doc.Root.Name = "Bookings";
var deserializer = new XmlSerializer(typeof(Bookings));
Bookings bookings;
using (var xmlReader = doc.CreateReader())
{
bookings = (Bookings)deserializer.Deserialize(xmlReader);
}
bookings.RootName = rootName.LocalName;
Note the use of CreateReader() to deserialize directly from the loaded XML.
Another option would be to create a custom XmlSerializer for each file using new XmlSerializer(typeof(Bookings), new XmlRootAttribute { ElementName = rootName }), however there are two issues with this:
Each serializer so created must be cached in a hash table to prevent a severe memory leak (loading of duplicate dynamically created assemblies), as is explained in this answer and also the documentation.
But even if you do this, if you are reading many different files with many different root names, your memory use will grow rapidly and continually due to constant loading of new unique dynamically created XmlSerializer assemblies.
However, if you only had to load one single file, you could do something like this:
Bookings bookings = null;
using (var xmlReader = XmlReader.Create(tempFile))
{
while (xmlReader.Read())
{
if (xmlReader.NodeType == XmlNodeType.Element)
{
var rootName = xmlReader.LocalName;
var deserializer = new XmlSerializer(typeof(Bookings), new XmlRootAttribute { ElementName = rootName });
bookings = (Bookings)deserializer.Deserialize(xmlReader);
bookings.RootName = rootName;
break;
}
}
}
If both solutions require too much memory, you could consider creating a subclassed XmlTextReader that allows for the the root element to be renamed on the fly while reading.
In both cases I modified Bookings to look as follows:
public class Bookings
{
[XmlIgnore]
public string RootName { get; set; }
[XmlAttribute("company")]
public string Company { get; set; }
[XmlElement("row")]
public List<Booking> StudentBookings { get; set; }
}

Can I serialize Xml for an MVC Web API without an object type?

So I'm writing my first mvc page, and I'm trying to write a series of routes to allow a reporting system to create simple reports. The xml is small, here is an example:
<xml><root><item><value>23</value></item></root>
I tried this:
using (StringWriter xmlStringWriter = new StringWriter())
{
using (XmlWriter xmlWriter = XmlWriter.Create(xmlStringWriter))
{
XmlWriter.WriteStartElement("root")
...
}
return xmlStringWriter.ToString();
}
but this obviously returns a string and is not interpreted as xml by the browser. I also know* that if you return an object that is serializable then the browser knows to interpret that as xml or json. So I tried defining a set of objects to hold each other in the way the xml is nested:
[Serializable]
public class XmlReportRoot
{
[System.Xml.Serialization.XmlAttribute("root")]
public List<XmlReportItem> item { get; set; }
}
[Serializable]
public class XmlReportItem
{
[System.Xml.Serialization.XmlAttribute("item")]
public XmlReportValue value { get; set; }
}
[Serializable]
public class XmlReportValue
{
[System.Xml.Serialization.XmlAttribute("value")]
public string count { get; set; }
}
and:
XmlReportRoot xmlRoot = new XmlReportRoot();
XmlReportItem xmlItem = new XmlReportItem();
List<XmlReportItem> itemList = new List<XmlReportItem>();
itemList.Add(xmlItem);
XmlReportValue xmlValue = new XmlReportValue();
xmlValue.count = newCustomers.ToString();
xmlItem.value = xmlValue;
xmlRoot.item = itemList;
XmlSerializer xmlSer = new XmlSerializer(typeof(XmlReportRoot));
xmlSer.Serialize(xmlRoot); //this line doesn't work
but this just feels wrong, and I couldn't quite get the serialization to work without worrying about a file stream, which I would rather do.
So I guess I was trying to find a way to do something like XmlWriter but be able to serialize that without an object type and return that, instead of having to worry about custom serializable objects types.
Use XmlWriter.Create(Response.OutputStream) and Response.ContentType = "application/xml"

How do I change root element name while keeping contents using XmlSerializer?

I have an XML document:
<data>
<elmt1>Element 1</elmt1>
<elmnt2>Element 2</elmnt2>
<elmnt3>Element 3</elmnt3>
</data>
I need to deserialize to an object that serializes to a different root name with everything else remaining the same.
For example:
<dataNew>
<elmt1>Element 1</elmt1>
<elmnt2>Element 2</elmnt2>
<elmnt3>Element 3</elmnt3>
</dataNew>
When serializing, we can always apply XmlRootAttribute to serialize to a different root name but I am not sure how to deserialize to a different XmlRootAttribute. It keeps failing error in document (1,2) pointing to the root attribute.
How can I achieve that?
If it's only the root name you want to change you can specify the root attribute when declaring the XmlSerializer.
XmlSerializer xmlSerializer = new XmlSerializer(typeof(data), new XmlRootAttribute("dataNew"));
XmlRootAttribute was supposed to work
[XmlRoot("dataNew")]
public class MyData()
{
[XmlElement("elmt1")]
public string myElement1{get;set;}
[XmlElement("elmnt2")]
public string myElement2{get;set;}
[XmlElement("elmtn3")]
public string myElement3{get;set;}
}
EDIT: Completed the XML
Did you try using the XmlAttributeOverrides class?
a sample of using XmlAttributeOverrides. If you vote up give one to hjb417 as well
class Program
{
static void Main(string[] args)
{
using (var fs = File.OpenRead("XmlFile1.xml"))
using (var fs2 = File.OpenRead("XmlFile2.xml"))
{
var xSer = new XmlSerializer(typeof(data));
var obj = xSer.Deserialize(fs);
//
var xattribs = new XmlAttributes();
var xroot = new XmlRootAttribute("dataNew");
xattribs.XmlRoot = xroot;
var xoverrides = new XmlAttributeOverrides();
xoverrides.Add(typeof(data), xattribs);
var xSer2 = new XmlSerializer(typeof(data), xoverrides);
var obj2 = xSer2.Deserialize(fs2);
}
}
}
public class data
{
public string elmt1 { get; set; }
public string elmnt2 { get; set; }
public string elmnt3 { get; set; }
}
You can use ExtendedXmlSerializer. This serializer support change root element name and property name.
If you have class like this:
[XmlRoot("dataNew")]
public class Data
{
[XmlElement("elmt1")]
public string Element1 { get; set; }
[XmlElement("elmnt2")]
public string Element2 { get; set; }
[XmlElement("elmtn3")]
public string Element3 { get; set; }
}
You can serialize it:
ExtendedXmlSerializer serializer = new ExtendedXmlSerializer();
var obj = new Data
{
Element1 = "A",
Element2 = "B",
Element3 = "C",
};
var xml = serializer.Serialize(obj);
Your xml will look like:
<?xml version="1.0" encoding="utf-8"?>
<dataNew type="Models.Example">
<elmt1>A</elmt1>
<elmnt2>B</elmnt2>
<elmtn3>C</elmtn3>
</dataNew>
ExtendedXmlSerializer has many other useful features:
Deserialization xml from standard XMLSerializer
Serialization class with property interface
Serialization circular reference and reference Id
Deserialization of old version of xml
Property encryption
Custom serializer
ExtendedXmlSerializer supports .net 4.5 and .net Core. You can integrate it with WebApi and AspCore.
You might have to implement ISerializable and change the root element in GetObjectData().

Categories

Resources