XML 序列化类似于 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();
        }
    }
}

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

现在,我使用 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 属性装饰我的类或执行其他解决方法之一。

无效操作异常

类型 OutputApp.Bar 不是预期的。使用 XmlInclude 或 属性,用于指定静态未知的类型。

恕我直言,XmlSerializer 的所有解决方法都不是一个很好的解决方案,我认为没有必要这样做,因为将对象序列化为 XML 没有蹩脚属性是完全可行的。

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

有什么想法吗?

更新1

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

更新2

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

下面有一条评论提到使用 object 作为数据类型是糟糕的设计。无论这是真的还是假的,这都是另一个讨论。关键是没有理由为什么它不应该能够序列化为 XML,我很想知道找到这样的解决方案。

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

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

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

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

同样使用 object 作为基类似乎是一种蹩脚的方法。至少定义一个标记接口:

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; }
}

Coz 现在it's pretty trivial在运行时获取所有引用的程序集中的所有加载类型,这些程序集正在实现标记接口并将它们传递给 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,它将知道如何正确序列化实现标记接口的所有类型。您已经实现了与 JSON.NET 几乎相同的功能。并且不要忘记,此 XmlSerializaer 实例化应驻留在知道所有加载类型的Composition Root project中。

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

相关内容

  • 没有找到相关文章

最新更新