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

标签 c# json xml serialization 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();
        }
    }
}

我有两个类(class)。 Foo 类和 Bar 类。类 Foo 有一个类型为 object 的属性。这是一个要求,因为它是一个可以容纳各种对象的契约(Contract),因此我不能将属性设置为具体类型或泛型。

现在我使用 CreateFooBar() 方法组成一个虚拟的 fooBar 对象。之后,我首先将其序列化为 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>

虽然这行得通,但肯定不是很好,因为我必须序列化为 json 之后才将其序列化为 xml。我想直接序列化成xml。

当我使用 XmlSerializer 执行此操作时,我得到了臭名昭著的 InvalidOperationExceptoin,因为我没有使用 XmlInclude 属性装饰我的类,也没有执行 other workarounds 之一.

InvalidOperationException

The type OutputApp.Bar was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.

XmlSerializer 的所有变通方法都不是一个很好的解决方案恕我直言,我不认为需要它,因为将对象序列化为 XML 没有 crappy 属性是完全可行的。

有谁知道 .NET 中可以执行此操作的优秀 Xml 序列化程序,或者是否有计划将此功能添加到 Json.Net?

有什么想法吗?

更新1

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

更新2

我要澄清的是,我不是在寻找一种解决方案来重构我的控制台应用程序以使其与 XmlSerializer 一起工作,我正在寻找一种方法来对我现有的内容进行 XML 序列化。

下面有一条评论提到使用 object 作为数据类型是糟糕的设计。不管这是真的还是假的,这是一个完全不同的讨论。关键是没有理由不能将其序列化为 XML,我很想找到这样的解决方案。

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

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

最佳答案

您不需要用 XmlInclude 属性污染您的模型。您可以向 XmlSerializer's constructor 显式指示所有已知类:

var serializer = new XmlSerializer(obj.GetType(), new[] { typeof(Bar) });

同时使用 object 作为基类似乎是一种糟糕的方法。至少定义一个标记接口(interface):

public interface IMarker
{
}

您的 Bar 将实现:

public class Bar : IMarker
{
    public int Arg1 { get; set; }
    public double Arg2 { get; set; }
}

然后将 Foo 类的 Value1 属性特化到这个标记,而不是让它成为宇宙中最通用的类​​型(不可能) :

public class Foo
{
    public IMarker Value1 { get; set; }
    public string Value2 { get; set; }
}

因为现在it's pretty trivial在运行时获取所有正在实现标记接口(interface)的引用程序集中的所有加载类型,并将它们传递给 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);

现在您已经有了一个功能非常强大的 XmlSerializer,它将知道如何正确序列化实现您的标记接口(interface)的所有类型。您已经实现了与 JSON.NET 几乎相同的功能。并且不要忘记这个 XmlSerializaer 实例化应该驻留在你的 Composition Root project 中。它知道所有加载的类型。

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

关于c# - 类似于 Json.Net 可以做的 XML 序列化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37071218/

相关文章:

c# - 如何将Json对象转换为Web服务中的自定义类对象?

xml - Ant |试图覆盖任务 javac 错误的旧定义

python - 从维基百科转储中提取平行文本

c# - 如何在图中找到最长路径

c# - CaSTLe Windsor自动注册多个接口(interface)及其对应的实现

json - SuperObject - 全部提取

java - 如何在jetty-util-ajax中使用JSON.toString()方法转义Unicode?

xml - 如何使用 log4net 配置 RollingFileAppender 以按日期和大小滚动?

C# socket重构(数据发送)

C# 如何将 Int ddmmyy 转换为 DateTime?