I have the following Console application:
using System;
using System.IO;
using System.Xml.Serialization;
using Newtonsoft.Json;
namespace OutputApp
{
public class Foo
{
public object Value1 { get; set; }
public string Value2 { get; set; }
}
public class Bar
{
public int Arg1 { get; set; }
public double Arg2 { get; set; }
}
class Program
{
public static Foo CreateFooBar()
{
return new Foo
{
Value1 = new Bar
{
Arg1 = 123,
Arg2 = 99.9
},
Value2 = "Test"
};
}
public static string SerializeXml(object obj)
{
using (var stream = new MemoryStream())
{
using (var reader = new StreamReader(stream))
{
var serializer = new XmlSerializer(obj.GetType());
serializer.Serialize(stream, obj);
stream.Position = 0;
return reader.ReadToEnd();
}
}
}
static void Main(string[] args)
{
var fooBar = CreateFooBar();
// Using Newtonsoft.Json
var json = JsonConvert.SerializeObject(fooBar, Formatting.Indented);
var xnode = JsonConvert.DeserializeXNode(json, "RootElement");
var xml = xnode.ToString();
// Using XmlSerializer, throws InvalidOperationException
var badXml = SerializeXml(fooBar);
Console.ReadLine();
}
}
}
I have two classes. Class Foo
and class Bar
. Class Foo
has a property of type object
. This is a requirement, because it is a contract which can hold a variety of objects and therefore I cannot set the property to a concrete type or a generic.
Now I compose a dummy fooBar
object using the CreateFooBar()
method. After that I first serialize it into JSON, which works wonderfully with Json.Net.
Then I use Json.Net's XML converter method to convert the json string into an XNode
object. It works great as well.
The output of both is the following:
{
"Value1": {
"Arg1": 123,
"Arg2": 99.9
},
"Value2": "Test"
}
<RootElement>
<Value1>
<Arg1>123</Arg1>
<Arg2>99.9</Arg2>
</Value1>
<Value2>Test</Value2>
</RootElement>
Now while this works, it is certainly not very nice, because I have to serialize into json only to serialize it into xml afterwards. I would like to serialize directly into xml.
When I use the XmlSerializer
to do this I get the infamous InvalidOperationExceptoin, because I did not decorate my classes with the XmlInclude
attribute or did one of the other workarounds.
InvalidOperationException
The type OutputApp.Bar was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.
None of the workarounds for the XmlSerializer is a good solution IMHO and I don't see the need for it as it is perfectly feasible to serialize an object into XML without crappy attributes.
Does anyone know a good Xml serializer in .NET which can do this or is there a plan to add this feature to Json.Net?
Any ideas?
Update1
I am not opposed to use attributes, but it needs to make sense. What I don't like about the XmlInclude
attribute is that it forces me into circular dependencies. Say I have assembly A which defines a base class, and assembly B which implements derived classes. Now the way the XmlInclude attribute works is that I'd have to decorate the base class in Assembly A with the type name of the child class from assembly B. This would mean I have a circular dependency and is a no go!
Update2
I shall clarify that I am not looking for a solution to re-factor my console application to make it work with the XmlSerializer
, I am looking for a way to XML serialize what I have there.
There was a comment below which mentions that using object
as a data type is poor design. Whether this is true or not, this is a whole other discussion. The point is that there is no reason why it shouldn't be able to serialize into XML and I am curious to find such a solution.
Personally I find creating a "marker" interface a dirty design. It abusing an interface to workaround the incapabilities of one single .NET class (XmlSerializer). If I would ever swap the serialization library for something else, then the whole marker interface would be redundant clutter. I don't want to couple my classes to one serializer.
I am looking for an elegant solution (if there is one)?
You don't need to pollute your models with XmlInclude attributes. You could explicitly indicate all known classes to the XmlSerializer's constructor
:
var serializer = new XmlSerializer(obj.GetType(), new[] { typeof(Bar) });
Also using object
as base class seems like a crappy approach. At least define a marker interface:
public interface IMarker
{
}
that your Bar
's will implement:
public class Bar : IMarker
{
public int Arg1 { get; set; }
public double Arg2 { get; set; }
}
and then then specialize the Value1
property of your Foo
class to this marker instead of making it like the most universal type in the universe (it can't be):
public class Foo
{
public IMarker Value1 { get; set; }
public string Value2 { get; set; }
}
Coz now it's pretty trivial
to get all loaded types at runtime in all referenced assemblies that are implementing the marker interface and passing them to the XmlSerializer constructor:
var type = typeof(IMarker);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p != type)
.Where(p => type.IsAssignableFrom(p))
.ToArray();
var serializer = new XmlSerializer(obj.GetType(), types);
Now you've got a pretty capable XmlSerializer
that will know how to properly serialize all types implementing your marker interface. You've achieved almost the same functionality as JSON.NET. And don't forget that this XmlSerializaer instantiation should reside in your Composition Root project
which knows about all loaded types.
And once again using object
is a poor design decision.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With