14

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

Community
  • 1
  • 1
dustinmoris
  • 3,195
  • 3
  • 25
  • 33
  • 1
    Since you are opposed to "crappy attributes", it sounds like your mind might be closed. Are you still willing to try any solution, even if is isn't perfect, in your opinion? – tgolisch May 06 '16 at 12:38
  • sure, but it has to make sense. What I don't like about the attributes 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. Boom, circular dependency. This is a no go! – dustinmoris May 06 '16 at 12:51
  • If you're using XmlSerializer, then there should be no problem with circular dependencies of any kind. Couldn't be simpler :) – ManoDestra May 06 '16 at 13:22

1 Answers1

8

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.

Community
  • 1
  • 1
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • Thanks for your input, but this is not what I am looking for. Please read my update above. – dustinmoris May 06 '16 at 13:45
  • If you think that using `object` in your models is a more elegant solution than a proper OOP design, well then, I am afraid that I cannot help you further. All I can do is wish you best of luck with the `XmlSerializer` class :-). If you don't want to use a marker interface at least don't you know statically all possible types that can be passed to `Value1`? – Darin Dimitrov May 06 '16 at 13:49
  • 1) Again, this is not a discussion about object, this is about serializing it. 2) I didn't say using object is better or worse than anything else. I just want to know how I could serialize what is there. You don't know the full story of why this might be relevant, so please stop trying to convince me of anything which is not part of this SO question. – dustinmoris May 06 '16 at 13:52
  • No of course I don't know the full story. It's your responsibility to provide it here if you expect any relevant answers. Since you haven't done so, I have provided a possible workaround for other people that might stumble upon this question. I am not trying to convince you in anything with my answer. What you should though more than definitely be convinced now after reading my answer is that you cannot achieve what you are looking for with the `XmlSerializer` class that's built into the framework. It provides you with some workarounds that I have outlined in my answer. – Darin Dimitrov May 06 '16 at 13:54
  • I would know what types need to be serialized. This is an option. Maybe the only one. Thanks for the suggestion! I would be tempted to leave this question un-answered for a bit longer to see if anyone else might have an idea, otherwise I will accept you answer. – dustinmoris May 06 '16 at 13:54
  • Other options include switching to a different XML serializer which would address your very specific requirements. I've done my best to propose the closest possible solution with the XML serializer of your choice at this time. – Darin Dimitrov May 06 '16 at 13:57
  • Yeah exactly. Sorry, maybe my question was phrased badly by me. I am actually looking for a .NET XML serializer, other than XmlSerializer, which can cope better with this scenario, similar like Json.Net. Do you know any which are worth a try? (I did a fair bit of googling, but didn't find anything) – dustinmoris May 06 '16 at 13:59
  • No, AFAIK there's no other built-in XML serializer into the .NET framework which would allow you to achieve this kind of serialization without specifying the known types somewhere. – Darin Dimitrov May 06 '16 at 14:03
  • No, by .NET serializer I mean a .NET library, but doesn't need to be built into the framework. I did not expect to find anything in the framework anyway. Anything from NuGet.org would be great, just like anything else. I just said ".NET serializer" because I didn't want people to suggest some COM components, etc. – dustinmoris May 06 '16 at 14:09
  • 1
    Sure, if you don't need it to be built into the framework here's one XML serializer you might checkout: http://www.sharpserializer.com/en/index.html. It is available as a NuGet as well: `Install-Package sharpserializer`. with this XML serializer you don't need to explicitly specify all known types. Sample usage: `new Polenter.Serialization.SharpSerializer().Serialize(fooBar, Console.OpenStandardOutput());`. – Darin Dimitrov May 06 '16 at 14:30
  • And that's just one example among others. All you need is google em up. – Darin Dimitrov May 06 '16 at 14:33