Deserialize XML Elements Return NULL - c#

I am trying to deserialize an XML file. However, I only want two elements from the file. Here's the basic markup:
<Stuff>
<Details>
<Comment>I want whats in here.</Comment>
<LogLevel>And here too.</LogLevel>
</Details>
<Stuff>
To deserialize I'm doing the following:
XmlSerializer deserializer;
FileStream stream = new FileStream(CONFIG_PATH, FileMode.Open);
XmlReader reader = new XmlTextReader(stream);
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = "Stuff";
xRoot.IsNullable = true;
// Details configuration area.
Utilities.Details d = new Utilities.Details();
deserializer = new XmlSerializer((typeof(Details)), xRoot);
d = (Details)deserializer.Deserialize(reader);
System.Windows.MessageBox.Show(d.Comment);
And finally the class that holds the objects:
/// <summary>
/// Configuration details.
/// </summary>
[Serializable()]
[XmlRoot(ElementName = "Details", IsNullable = true)]
public sealed class Details
{
public Details()
{
}
[XmlElement("Comment")]
public string Comment { get; set; }
[XmlElement("LogLevel")]
public string LogLevel { get; set; }
}
However d.Comment and d.LogLevel continue to return null no matter what I do. Any ideas?

With that setup, it expects
<Stuff>
<Comment>....
<LogLevel>...
...
To handle two levels in the XML you will need an object model that matches. Rather than messing with the XmlRootAttribute at runtime, write a type Stuff that has a Details instance in a property called Details. Then create the serializer to expect a Stuff instance:
public class Stuff {
public Details Details {get;set;}
}
An alternative approach would be to use a sub-reader over the input, but that is harder.

I ran into a lot of similar issues when I was attempting to use XmlSerializer and FileStreams.
I would suggest changing this to Linq to XML. I found it to be a lot easier and faster to learn.
Here is a great Video by Mike Taulty
Linq to XML Tutorial

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?.

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

How To Track Down Deserialize XML to Object Error in XSD?

This is a sample of my xml file:
<IFX xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="finalizacaoOrcamentoVO">
<dadosOrcamento>...</dadosOrcamento>
<faturamento>...</faturamento>
</IFX>
This is my auto-generated by Visual Studio object class:
/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class IFX
{
private IFXDadosOrcamento dadosOrcamentoField;
private IFXFaturamento faturamentoField;
But I've been getting this error every time I try to deserialize:
Message "Error document XML (1, 57)." string
This is my deserialize method:
IFX document;
XmlSerializer serializer = new XmlSerializer(typeof(object));
using (var reader = XmlReader.Create(file.InputStream))
{
document = (IFX)serializer.Deserialize(reader);
}
Any hint on what should be fixed?
Thanks in advance!
These are my subclasses:
ClassObject
The xsi:type attribute, short for {http://www.w3.org/2001/XMLSchema-instance}type, is a w3c standard attribute that is used to explicitly assert the type of its element. As explained in Xsi:type Attribute Binding Support, XmlSerializer interprets this to mean that the element is serialized from a polymorphic derived type of the expected type.
I.e. if your XML looks like:
<IFX xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="finalizacaoOrcamentoVO">
<dadosOrcamento>
<IFXDadosOrcamentoValue>A IFXDadosOrcamentoValue</IFXDadosOrcamentoValue>
</dadosOrcamento>
</IFX>
Then XmlSerializer expects that the following classes will exist:
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
[XmlInclude(typeof(finalizacaoOrcamentoVO))]
public partial class IFX
{
private IFXDadosOrcamento dadosOrcamentoField;
public IFXDadosOrcamento dadosOrcamento { get { return dadosOrcamentoField; } set { dadosOrcamentoField = value; } }
}
public class finalizacaoOrcamentoVO : IFX
{
// This derived type may have some or all of the properties shown as elements in the XML file.
}
public class IFXDadosOrcamento
{
public string IFXDadosOrcamentoValue { get; set; }
}
Where finalizacaoOrcamentoVO is a type that inherits from IFX.
Note the presence of [XmlInclude(typeof(finalizacaoOrcamentoVO))]. This attribute informs the serializer of the subtypes that might be encountered and must be present for every allowed subtype.
Having done this, the XML can now be deserialized via:
IFX document;
XmlSerializer serializer = new XmlSerializer(typeof(IFX));
using (var reader = XmlReader.Create(inputStream))
{
document = (IFX)serializer.Deserialize(reader);
}
An instance of finalizacaoOrcamentoVO will actually get created thereby.
That being said, in your comment you state that your auto-generated classes do not include a derived type finalizacaoOrcamentoVO. If you generated the classes from an XSD, then the XSD and XML do not match and you should get that resolved. If you generated the classes by pasting this very XML into Visual Studio, then there might be a bug or limitation in Visual Studio's code generation, which can be fixed manually as shown above.
If you really want to ignore the xsi:type attribute, then you will have to do so manually, since support for it is built-in to the serializer. One way is to load into an intermediate XDocument:
XDocument xDoc;
using (var reader = XmlReader.Create(inputStream))
{
xDoc = XDocument.Load(reader);
}
var attr = xDoc.Root.Attribute("{http://www.w3.org/2001/XMLSchema-instance}type");
if (attr != null)
attr.Remove();
var document = xDoc.Deserialize<IFX>();
Using the extension method:
public static class XObjectExtensions
{
public static T Deserialize<T>(this XContainer element, XmlSerializer serializer = null)
{
if (element == null)
throw new ArgumentNullException();
using (var reader = element.CreateReader())
return (T)(serializer ?? new XmlSerializer(typeof(T))).Deserialize(reader);
}
}

Deserialization XML to object with list in c#

I want to deserialize XML to object in C#, object has one string property and list of other objects.
There are classes which describe XML object, my code doesn't work (it is below, XML is at end of my post). My Deserialize code doesn't return any object.
I think I do something wrong with attributes, could you check it and give me some advice to fix it.
Thanks for your help.
[XmlRoot("shepherd")]
public class Shepherd
{
[XmlElement("name")]
public string Name { get; set; }
[XmlArray(ElementName = "sheeps", IsNullable = true)]
[XmlArrayItem(ElementName = "sheep")]
public List<Sheep> Sheeps { get; set; }
}
public class Sheep
{
[XmlElement("colour")]
public string colour { get; set; }
}
There is C# code to deserialize XML to objects
var rootNode = new XmlRootAttribute();
rootNode.ElementName = "createShepherdRequest";
rootNode.Namespace = "http://www.sheeps.pl/webapi/1_0";
rootNode.IsNullable = true;
Type deserializeType = typeof(Shepherd[]);
var serializer = new XmlSerializer(deserializeType, rootNode);
using (Stream xmlStream = new MemoryStream())
{
doc.Save(xmlStream);
var result = serializer.Deserialize(xmlStream);
return result as Shepherd[];
}
There is XML example which I want to deserialize
<?xml version="1.0" encoding="utf-8"?>
<createShepherdRequest xmlns="http://www.sheeps.pl/webapi/1_0">
<shepherd>
<name>name1</name>
<sheeps>
<sheep>
<colour>colour1</colour>
</sheep>
<sheep>
<colour>colour2</colour>
</sheep>
<sheep>
<colour>colour3</colour>
</sheep>
</sheeps>
</shepherd>
</createShepherdRequest>
XmlRootAttribute does not change the name of the tag when used as an item. The serializer expects <Shepherd>, but finds <shepherd> instead. (XmlAttributeOverrides does not seem to work on arrays either.) One way to to fix it, is by changing the case of the class-name itself:
public class shepherd
{
// ...
}
An easier alternative to juggling with attributes, is to create a proper wrapper class:
[XmlRoot("createShepherdRequest", Namespace = "http://www.sheeps.pl/webapi/1_0")]
public class CreateShepherdRequest
{
[XmlElement("shepherd")]
public Shepherd Shepherd { 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"

Categories

Resources