我有以下控制台应用程序:
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
是一个糟糕的设计决策。