XML序列化类似于Json.Net可以做的

dus*_*ris 5 c# xml serialization json json.net

我有以下控制台应用程序:

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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我有两节课.班级Foo和班级Bar.类Foo具有类型的属性object.这是一个要求,因为它是一个可以容纳各种对象的契约,因此我无法将属性设置为具体类型或通用属性.

现在我fooBar使用该CreateFooBar()方法组成一个虚拟对象.之后我首先将其序列化为JSON,它与Json.Net完美配合.然后我使用Json.Net的XML转换器方法将json字符串转换为XNode对象.它的效果也很好.

两者的输出如下:

{
  "Value1": {
    "Arg1": 123,
    "Arg2": 99.9
  },
  "Value2": "Test"
}

<RootElement>
  <Value1>
    <Arg1>123</Arg1>
    <Arg2>99.9</Arg2>
  </Value1>
  <Value2>Test</Value2>
</RootElement>
Run Code Online (Sandbox Code Playgroud)

现在虽然这有效,但肯定不是很好,因为我必须序列化为json,然后才将其序列化为xml.我想直接序列化为xml.

当我使用它XmlSerializer来做到这一点时,我得到臭名昭着的InvalidOperationExceptoin,因为我没有用XmlInclude属性装饰我的类或做了其他一个解决方法.

出现InvalidOperationException

不期望输出类型OutputApp.Bar.使用XmlInclude或SoapInclude属性指定静态未知的类型.

没有XmlSerializer的解决方案是一个很好的解决方案恕我直言,我没有看到它的需要,因为将对象序列化为XML而没有蹩脚的属性是完全可行的.

有没有人知道.NET中的一个好的Xml序列化程序可以做到这一点,还是有计划将此功能添加到Json.Net?

有任何想法吗?

UPDATE1

我不反对使用属性,但它需要有意义.我不喜欢这个XmlInclude属性是因为它迫使我进入循环依赖.假设我有定义基类的程序集A和实现派生类的程序集B. 现在XmlInclude属性的工作方式是我必须用程序集B中的子类的类型名称来装配程序集A中的基类.这意味着我有一个循环依赖,并且是不行!

UPDATE2

我将澄清,我不是在寻找一个解决方案来重新调整我的控制台应用程序以使其与它一起工作XmlSerializer,我正在寻找一种XML序列化我在那里的方法.

下面有一条评论提到使用object数据类型的设计很差.无论这是否属实,这都是另一个讨论.关键是没有理由不能将它序列化为XML,我很想找到这样的解决方案.

我个人认为创建一个"标记"界面是一个肮脏的设计.它滥用一个接口来解决单个.NET类(XmlSerializer)的无能力问题.如果我将序列化库换成其他东西,那么整个标记接口将是多余的混乱.我不想将我的类耦合到一个序列化器.

我正在寻找一个优雅的解决方案(如果有的话)?

Dar*_*rov 5

您不需要使用XmlInclude属性来污染您的模型。您可以向明确指出所有已知的类XmlSerializer's constructor

var serializer = new XmlSerializer(obj.GetType(), new[] { typeof(Bar) });
Run Code Online (Sandbox Code Playgroud)

object基类用作基类似乎也很糟糕。至少定义一个标记接口:

public interface IMarker
{
}
Run Code Online (Sandbox Code Playgroud)

Bar的将实现:

public class Bar : IMarker
{
    public int Arg1 { get; set; }
    public double Arg2 { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后将Value1您的Foo类的属性专用于此标记,而不是使其成为Universe中最通用的类​​型(不能):

public class Foo
{
    public IMarker Value1 { get; set; }
    public string Value2 { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

现在it's pretty trivial,Coz 可以在运行时在所有实现了标记接口的引用程序集中获取所有已加载类型,并将它们传递给XmlSerializer构造函数:

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);
Run Code Online (Sandbox Code Playgroud)

现在,您已经拥有了相当强大的能力XmlSerializer,它将知道如何正确地序列化实现标记器接口的所有类型。您已经实现了与JSON.NET几乎相同的功能。并且不要忘记,此XmlSerializaer实例化应该驻留在您的实例中,该实例Composition Root project知道所有已加载的类型。

再次使用object是一个糟糕的设计决策。