Is this too ... hacky? Xml to an object - c#

I'm playing with my favorite thing, xml (have the decency to kill me please) and the ultimate goal is save it in-house and use the data in a different manner (this is an export from another system). I've got goo that works, but meh, I think it can be a lot better.
public Position(XElement element)
{
Id = element.GetElementByName("id");
Title = element.GetElementByName("title");
}
I'm thinking of making it more automated (hacky) by adding data annotations for the xml element it represents. Something like this for instance.
[XmlElement("id")]
public string Id { get; set; }
[XmlElement("title")]
public string Title { get; set; }
then writing some reflection/mapper code but both ways feels ... dirty. Should I care? Is there a better way? Maybe deserialize is the right approach? I just have a feeling there's a mo' bettah way to do this.

You can use the XmlSerializer class and markup your class properties with attributes to control the serialization and deserialization of the objects.
Here's a simple method you can use to deserialize your XDocument into your object:
public static T DeserializeXml<T>(XDocument document)
{
using (var reader = document.CreateReader())
{
var serializer = new XmlSerializer(typeof (T));
return (T) serializer.Deserialize(reader);
}
}
and a simple serializer method:
public static String ToXml<T>(T instance)
{
using (var output = new StringWriter(new StringBuilder()))
{
var serializer = new XmlSerializer(typeof(T));
serializer.Serialize(output, instance);
return output.ToString();
}
}

The mechanism that you are suggesting already exists in the form of the XmlSerializer class and a set of custom attributes that you can use to control the serialisation process.
In fact the .Net framework comes with a tool xsd.exe which will generate these class files for you from a schema definition (.xsd) file.
Also, just to make things really easy, Visual Studio even has the ability to generate you an .xsd schema for an xml snippet (its hidden away in the Xml menu somewhere).

Related

Deserializing XML when the root type is the same but the inner XML can represent different types

I have to work on a application when sends XML back. Because the root is always the "Reply", but the property's differ, deserializing can't be based on 1 object type.
I've now written code which loads the Xml first in a new XmlDocument, reads a Name attribute and based on the attribute, I try to deserialize it. Are there
better ways?
Example xml I can expect:
<Reply Name="GetModulesList" Result="yes"><ModuleName="xxxxxx.exe" Path="\Debug\xxxxxxx.exe" Order="1"/></Reply>
<Reply Name="OpenRecipe" Result="yes"/>
How whould you solve this?
XmlDocument doc = new XmlDocument();
doc.LoadXml(trimmedPart);
if (doc.DocumentElement?.Attributes != null)
{
XmlAttribute name = doc.DocumentElement.Attributes.Cast<XmlAttribute>().SingleOrDefault(a => String.Compare(a.Name, "name", StringComparison.OrdinalIgnoreCase) == 0);
replyName = name?.Value;
}
if (!string.IsNullOrEmpty(replyName))
{
XmlSerializer serializer = new XmlSerializer(GetSerializerObjectType(replyName));
using (StringReader reader = new StringReader(trimmedPart))
{
object obj = serializer.Deserialize(reader);
if (obj != null) returnList.Add(obj);
}
}
I recently dealt with a similar scenario. Here's a very rough outline, adapted from what I was working with to your case as specifically as I can.
My assumption is that you're going to do different things with different types. If it's an OpenRecipe you're going to do one thing, if it's GetModuleList you're going to do something else.
The biggest difference is that each "handler" I worked with for different types might handle completely different types of data - XML, JSON, even Excel, so the handlers received content in the form of a byte array and were responsible for deserializing it. (It wasn't my idea to write an in-house version of Biztalk.)
In this case, if it's always XML, you can
Determine the type of the inner XML from the Name attribute
Deserialize the inner XML to that type
Pass it off to a strongly-typed class
This class is concerned with deserializing the Reply (except for the unknown inner content) and passing it to something that will handle that inner content in a more strongly typed way:
public class XmlReplyRouter
{
private readonly IReplyTypeMapper _replyTypeMapper;
private readonly IHandlerFactory _handlerFactory;
private readonly XmlSerializer _serializer = new XmlSerializer(typeof(Reply));
public XmlReplyRouter(
IReplyTypeMapper replyTypeMapper,
IHandlerFactory handlerFactory)
{
_replyTypeMapper = replyTypeMapper;
_handlerFactory = handlerFactory;
}
public void RouteReply(string replyXml)
{
using (var reader = new StringReader(replyXml))
{
var reply = (Reply)_serializer.Deserialize(reader);
var replyType = _replyTypeMapper.GetReplyType(reply.Name);
var handler = _handlerFactory.GetHandler(replyType);
handler.HandleReply(reply);
}
}
}
public class Reply
{
[XmlAttribute]
public string Name { get; set; }
[XmlAttribute]
public string Result { get; set; }
[XmlAnyElement]
public XmlNode InnerXml { get; set; }
public string XmlContent => InnerXml?.OuterXml;
}
That's where all the ugliness goes.
The XmlAnyElement attribute allows us to deserialize without knowing what to make of that inner content. Once we know what type to use we can separately deserialize that.
The implementations of IReplyTypeMapper and IHandlerFactory could be anything. In my case I couldn't use a DI container so it contained a Dictionary<string, IReplyHandler> and selected the correct one based on the name. (The terminology I worked with was different, but same concept.)
The first determines the type (from the "name" attribute) and the second returns a handler for that type.
public interface IReplyTypeMapper
{
Type GetReplyType(string replyName);
}
public interface IHandlerFactory
{
IReplyHandler GetHandler(Type contentType);
}
Finally, here's the interface and a base class for the reply handlers.
As you can see, the base class doesn't really do anything. It just bridges the gap between object and generic types so that we can write strongly-typed handlers.
public interface IReplyHandler
{
void HandleReply(object content);
}
public abstract class BaseHandler<T> : IReplyHandler
{
private readonly XmlSerializer _serializer = new XmlSerializer(typeof(T));
public void HandleReply(object content)
{
HandleReplyContent((T)content);
}
protected abstract void HandleReplyContent(T content);
}
As you can see it's very similar to what you're doing. The objective, though, is that once I've gotten this minimal amount of initial code out of the way, everything else is a strongly-typed class like this:
public class SomeSpecificReplyHandler : BaseHandler<SomeSpecificReply>
{
// Maybe these have dependencies of their own. That's easiest if
// everything gets resolved from an IoC container.
protected override void HandleReplyContent(SomeSpecificReply content)
{
// do whatever
}
}
I don't know if this might be overkill for your needs. My intent was to start from an entry point where my data could be just about anything and quickly get out of there to a place where all of my code was strongly-typed and easy to test. All of these classes are testable (I wrote a few while typing this up) the individual handlers would also be easy to test.
This post contains some other details about implementing the factories without an IoC container. In my case I couldn't work with an IoC container, so I had to "manually" compose all of my classes. The inner implementation of the factory was just a dictionary. That way if I had the option of switching to an IoC container later I could replace the whole thing with DI registrations.

Deserializing Generic List<T> From XML Without XmlAttributes

I am trying to deserialize an xml list to a generic List in C#. I do not want to use XmlAttributes as the solution needs to be generic. I must be getting close as I am getting 3 Item objects but they are all populated with default values, ie: null or 0.
This is not a duplicate of how to deserialize an xml node with a value and an attribute using asp.net serialization which uses XmlAttributes and is not an option. This is clearly stated in the question.
There are a number of SO answers out there whicha I have tried and are similar to what I need, including the other list of SO responses in the [Duplicate]. The code below is modified from one of those answers but does not quite work.
<?xml version="1.0" encoding="utf-8"?>
<root>
---snip---
<Items>
<Item ItemId="1" ItemName="TestName1" Number="100" Created=""></Item>
<Item ItemId="2" ItemName="TestName2" Number="200" Created=""></Item>
<Item ItemId="3" ItemName="TestName3" Number="300" Created=""></Item>
</Items>
</root>
========================================================
var sourceData = XDocument.Parse(xml);
public List<T> GetObjectList<T>(string identifier) where T : class, new()
{
if (sourceData != null && !identifier.isNullOrEmpty())
{
var list = sourceData.Descendants(identifier)
.Select(p => DeserializeObject<T>(p.ToString()))
.ToList();
return list;
}
return new List<T>();
}
public static T DeserializeObject<T>(string xml) where T : class, new()
{
if (string.IsNullOrEmpty(xml))
{
return default(T);
}
try
{
using (var stringReader = new StringReader(xml))
{
var serializer = new XmlSerializer(typeof(T));
return (T) serializer.Deserialize(stringReader);
}
}
catch
{
return default(T);
}
}
public class Item
{
public int ItemId { get; set; }
public string ItemName { get; set; }
public int? Number { get; set;}
public DateTime? Created { get; set; }
}
// RESULT
// Item ItemId="0" ItemName="null" Number="null" Created="null"
// Item ItemId="0" ItemName="null" Number="null" Created="null"
// Item ItemId="0" ItemName="null" Number="null" Created="null"
Check out the XmlSerialzer constructor, you can specify attribute overrides there, if you do not want to modify the class -- you'll need some data defining what you serialize at some point; the XmlSerializer was never written to work without these Attributes, or types that are known to be serializable (numbers, strings, ...).
If you want to be generic, you'll have to write your own serializer, which uses reflection to decide what to serialize, how, and in which order, and what to ignore. You can use the XmlSerializer in it when serializing types that have the necessary attributes. Search for tutorials explaining how to implement IXmlSerializable, they will show you how to use XmlWriter / XmlReader to avoid handling the XML syntax yourself.
A guess why it is like this: Circular references make (de-)serialization really hard, and a generic solution would probably try to serialize this without coming to an end, causing weird bugs with the call hanging forever, when some developer changes one of those classes without being able to see (due to no attribute being there) that they have to write the class in a way avoiding serialization problems.
I asked another similar question (A Better XElement to Object Without XMLAttributes in C#) when this one got marked as a duplicate (for a while) and ended up creating a simple custom de-serializer that met my needs. See the answers there for the code if interested.

How can I deserialize xml with 2 different types of the same xml element and property name?

In the database, we have an xml field that contains 2 validation schemas; the old one does not have a namespace, the new one does. The reason for this is that we had to version one of the properties. Here is an example of the differences:
Version 1
<PropertyA>
<PropertyA1>false</PropertyA1>
<PropertyA2>3.23</PropertyA2>
</PropertyA>
Version 2
<ts:PropertyA xmlns:ts="http://www.example.com/v2">
<ts:PropertyA1>false</ts:PropertyA2>
<ts:PropertyA2>
<ts:PropertyA2a>
<ts:PropertyA2a1>City 1</ts:PropertyA2a1>
<ts:PropertyA2a2>3.23</ts:PropertyA2a2>
</ts:PropertyA2a>
<ts:PropertyA2b>
<ts:PropertyA2b1>City 2</ts:PropertyA2b1>
<ts:PropertyA2b2>1.21</ts:PropertyA2b2>
</ts:PropertyA2b>
</ts:PropertyA2>
</ts:PropertyA>
Basically, we just create multiple options for PropertyA2...
So now the isue is deserialization. This object needs to be deserialized into the same data object in the app code and the problem is that the element name is the same so the serializer is obviously having trouble figuring out which object to deserialize into since sometimes the database will return Version 1 and sometimes it will return Version 2.
Here is an example of the data class being used for serialization and my current approach that isn't quite working:
[Serializable]
public class MyDataClass
{
// ... other stuff
[XmlElement(Name = "PropertyA", typeof(V1.PropertyA), Namespace = "")]
public V1.PropertyA PropertyAV1 { get ;set; }
[XmlElement(Name = "PropertyA", typeof(V2.PropertyA), Namespace = "http://www.example.com/v2")]
public V2.PropertyA PropertyAV2 { get; set; }
}
[Serializable]
public class V1.PropertyA
{
public bool PropertyA1 { get; set; }
public decimal PropertyA2 { get; set; }
}
[Serializable]
public class V2.PropertyA
{
public bool PropertyA1 { get; set; }
public List<SomeOtherThing> PropertyA2 { get; set; }
}
When I go to deserialize V1, it works fine. When I go to deserialize V2, i get an error Did not expect <ts:PropertyA xmlns:ts="http://www.example.com/v2"> so I'm thinking there's a parameter I'm missing in the deserialize method:
public MyDataClass Deserialize(string xml)
{
var s = new XmlSerializer(typeof (MyDataClass));
MyDataClass info = null;
using (var r = new StringReader(xml))
{
info = (MyDataClass) s.Deserialize(r);
}
return info;
}
I believe you can set the expected namespace in the serializer, but since I don't know what the namespace is going to be until I actually inspect the xml document, I'm not sure how to proceed.
So my question is this: Is what I'm trying to do even possible? Am I on the right track? Is there a better solution that is maybe less contrived? How can I have the serializer deal with the new namespace and deserialize to the correct properties?
You can't.
The problem here is that you have to hardcode MyDataClass according to a single XMLSchema. If the XMLSchema alters, MyDataClass is no longer a valid target for the XMLSerializer's deserialize method, which is why you're getting the 'Did not expect ...' error message. In this case, when reading the V2 xml data stream, the deserialize method tries to fill MyDataClass#PropertyAV1 with the content of <ts:PropertyA2> and there is no way of telling it to instead fill MyDataClass#PropertyAV2. Even if there was a way to achieve this, you'd be stuck with an undefined value for MyDataClass#PropertyAV1 in the object of type MyDataClass.
So there are two solutions to the problem at hand :
a) Stick with XMLSerializer and define class MyDataClass like so
public class MyDataClass
{
// only one Property here, as there's only one root element in the xml
// and this single Property is not bound to a specific XML element
[XmlAnyElement]
public PropertyA PropertyA { get ;set; }
}
You then have to analyze the contents of PropertyA yourself and build some logic around it, see here for more details :
http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlanyelementattribute.aspx
b) Dispense the XMLSerializer, read the XML data stream with XMLReader and do the all the parsing of the xml yourself, also add logic to create the according C# objects, depending on the the type of xml you've read.
Obviously, both solutions require more coding on the C# side, but with solution b) you'll have the chance of gaining a performance benefit, as XMLSerializer#deserialize most probably builds a DOM tree to create the C# object from, which the XMLReader doesn't do.
It seems that what I was trying to do was either unachievable or no one with the right level of xml fu saw this thread :(.
So anyway, what I ended up doing was adding an extra column to the database with the version number of the xml contract. Since everything in there was the same, I just called it V1.
I then read that info out into app code and used the version number to drive a factory. Basically, if v1, then deserialize to this, if v2, deserialize to this other thing.
And of course, to support that, I simply created a new data object that had the appropriate structure to support v2. I'm not happy with it, but it works and is flexible enough :/

C# XML Serialization - weakening encapsulation?

Am I correct in thinking that, in order to get C# to serialize an object, I MUST have a public property for every field that needs its state stored?
If so, is that not very very sucky, as it weakens (if not breaks entirely) any encapsulation my class has?
In Java, XStream can iterate over every non-transient field and archive it. In C# this can't happen, and just to make things worse, things like Dictionaries don't serialize AT ALL. It's all a bit of a mess, no?
I've seen the DLL for a "port" of XStream to .net, but there are no docs and I'm suspicious.
You should use DataContractSerializer, and mark every property/field you want to serialize with [DataMember]. It doesn't care if your fields are private or public. By the way you can serialize Dictionaries with it...
[DataContract]
public class MyClass
{
[DataMember]
private string _privateField;
[DataMember]
public int PublicProperty { get; set;}
}
Serialization :
private static string SerializeXml<T>(T item)
{
DataContractSerializer ser = new DataContractSerializer(item.GetType());
StringBuilder sb = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings { OmitXmlDeclaration = true, ConformanceLevel = ConformanceLevel.Fragment };
using (XmlWriter writer = new XmlWriter(sb, settings))
{
ser.WriteObject(writer, item);
}
return sb.ToString();
}
Look here for differences between XmlSerializer and DataContractSerializer : http://www.danrigsby.com/blog/index.php/2008/03/07/xmlserializer-vs-datacontractserializer-serialization-in-wcf/
The binaryformatter serializes private and even readonly fields without the need for properties. The XmlSerializer can only serialize with a public no-arg constructor and public properties. If you want to use XmlSerializer with encapsulation you can use IXmlSerializable, but that is rather painful.
If your object model is fairly simple or you can make it fairly simple by introducing special DTO:s for serialization (e.g. to avoid structs), then I recommend using a contract based serializer that can serialize private fields or properties. Have a look at protobuf-net.

XML Serialization empty collections not self closing

Wondering if anyone can help me with this annoying but trivial (in terms of need) question.
I have an object which has inside it a collection of object
public class OuterClass
{
InnerClasses innerClasses= new InnerClasses();
public InnerClasses InnerClasses
{
get {return innerClasses; }
}
public string Name
{
get;set;
}
}
public class InnerClasses:List<InnerClass>
{
}
public class <InnerClass>
{
}
basically my problem I'm experiencing is that if I pass it through an xml serializer
var outer = new OuterClass(){Name="Name"}
var xmlSerializer = new XmlSerializer(GetType());
var stringBuilder = new StringBuilder();
var stringWriter = new StringWriter(stringBuilder);
xmlSerializer.Serialize(stringWriter, this);
return stringBuilder.ToString();
I'm wondering why when I have no inner classes it puts out
<OuterClass>
<Name>Name</Name>
<InnerClasses ></InnerClasses>
</OuterClass>
why does it not put InnerClasses as a self closed tag ?
I realize the code above will put but I can't put the full actual code listing. (not much use I know) I'm just looking for pointers as to what could cause it.
I can't for the life of me work out why it's not doing this by default.
Many thanks for any input as to where to look.
I've noticed the same thing all the time, but as far as I've found it's just the way the Serialization class was implemented. They simply chose to not use self closing tags. Hopefully this will be changed in future implementations.

Categories