具有多态对象的数组的JSON序列化
使用.NET标准的JavascriptSerializer/JsonDataContractSerializer或外部解析器可以使用包装器方法(包括对象类型)序列化对象数组吗?具有多态对象的数组的JSON序列化
例如,从列表生成此JSON:
[{ 'dog': { ...dog properties... } },
{ 'cat': { ...cat properties... } }]
代替典型:
[{ ...dog properties... },
{ ...cat properties... }]
这与使用JsonTypeInfo.As.WRAPPER_OBJECT属性杰克逊在Java中是可行的。
也许我看到的最接近的是使用JavaScriptSerializer
并将JavaScriptTypeResolver
传递给构造函数。它不会生成与您的问题完全相同的JSON格式,但它确实有一个_type
字段,它描述了要序列化的对象的类型。它可能会变得有点难看,但也许它会为你制造诡计。
这里是我的示例代码:
public abstract class ProductBase
{
public String Name { get; set; }
public String Color { get; set; }
}
public class Drink : ProductBase
{
}
public class Product : ProductBase
{
}
class Program
{
static void Main(string[] args)
{
List<ProductBase> products = new List<ProductBase>()
{
new Product() { Name="blah", Color="Red"},
new Product(){ Name="hoo", Color="Blue"},
new Product(){Name="rah", Color="Green"},
new Drink() {Name="Pepsi", Color="Brown"}
};
JavaScriptSerializer ser = new JavaScriptSerializer(new SimpleTypeResolver());
Console.WriteLine(ser.Serialize(products));
}
}
而结果是这样的:
[
{"__type":"TestJSON1.Product, TestJSON1, Version=1.0.0.0, Culture=neutral, Publ
icKeyToken=null","Name":"blah","Color":"Red"},
{"__type":"TestJSON1.Product, Test
JSON1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null","Name":"hoo","Colo
r":"Blue"},
{"__type":"TestJSON1.Product, TestJSON1, Version=1.0.0.0, Culture=neu
tral, PublicKeyToken=null","Name":"rah","Color":"Green"},
{"__type":"TestJSON1.Dr
ink, TestJSON1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null","Name":"P
epsi","Color":"Brown"}
]
我使用SimpleTypeConverter,默认情况下为框架的一部分。您可以自行创建,以缩短__type
返回的内容。
编辑:如果我创造我自己的JavaScriptTypeResolver
缩短类型名称回来后,我会产生这样的:
[
{"__type":"TestJSON1.Product","Name":"blah","Color":"Red"},
{"__type":"TestJSON1.Product","Name":"hoo","Color":"Blue"},
{"__type":"TestJSON1.Product","Name":"rah","Color":"Green"},
{"__type":"TestJSON1.Drink","Name":"Pepsi","Color":"Brown"}
]
使用该转换器类:
public class MyTypeResolver : JavaScriptTypeResolver
{
public override Type ResolveType(string id)
{
return Type.GetType(id);
}
public override string ResolveTypeId(Type type)
{
if (type == null)
{
throw new ArgumentNullException("type");
}
return type.FullName;
}
}
而只是路过它进入我的JavaScriptSerializer
构造函数(而不是SimpleTypeConverter
)。
我希望这有助于!
谢谢你的详细答案(我会upvote)。我意识到这个解决方案,但希望与在Java中实现的客户端互操作,但无法接收这些“__type”属性。 – ggarber 2011-03-04 08:48:04
非常感谢你,这正是我一直在寻找的! – 2013-02-15 14:25:48
@PeterMorris - 不客气。我最终也写了一篇关于它的博客文章。这是一个有趣的问题要解决。 http://geekswithblogs.net/DavidHoerster/archive/2012/01/06/json.net-and-deserializing-anonymous-types.aspx – 2013-02-15 14:50:29
Json.NET对此有一个很好的解决方案。还有就是增加了智能型信息的设置 - 声明它是这样的:
new JsonSerializer { TypeNameHandling = TypeNameHandling.Auto };
这将决定是否需要类型嵌入并添加必要。假设我有以下类:
public class Message
{
public object Body { get; set; }
}
public class Person
{
public string Name { get; set; }
}
public class Manager : Person
{
}
public class Department
{
private List<Person> _employees = new List<Person>();
public List<Person> Employees { get { return _employees; } }
}
注意消息体是对象类型的,而且管理器子类是Person。如果我用序列一处身一个消息,有一个经理,我得到这样的:
{
"Body":
{
"$type":"Department, MyAssembly",
"Employees":[
{
"$type":"Manager, MyAssembly",
"Name":"Tim"
}]
}
}
注意它是如何增加$ type属性描述部和经理的类型。如果我现在一个人添加到雇员名单,并更改消息体的类型为系这样的:
public class Message
{
public Department Body { get; set; }
}
则不再需要体型注释和新的人没有被标注 - 没有注释假定元素实例是已声明的数组类型。序列化格式变为:
{
"Body":
{
"Employees":[
{
"$type":"Manager, MyAssembly",
"Name":"Tim"
},
{
"Name":"James"
}]
}
}
这是一种高效方法 - 类型注释仅在必要时添加。虽然这是.NET特定的,但这种方法足够简单,可以轻松地处理其他平台上的反序列化器/消息类型,以便处理此问题。
虽然我在公开的API中使用这个,但是它是非标准的,因为它是非标准的。在这种情况下,您希望避免多态,并在消息中使版本和类型信息具有非常明确的属性。
可以创建一个SerializationBinder派生类,它可以为类型映射更友好的字符串。这然后绑定到SerializerSettings.Binder属性。这还没有我想要的那么干净。如果有一个属性可以添加到类中以指定类型代码应该是什么,那将是非常好的。 – 2013-12-11 15:21:20
1)您可以使用字典<字符串对象>做的工作,...
[{ “猫”:{ “名称”: “粉红”}},{“猫“:{” 名称 “:” 闪闪 “}},{” 狗 “:{” 名称 “:” 最大“}}]
public class Cat
{
public string Name { get; set; }
}
public class Dog
{
public string Name { get; set; }
}
internal static void Main()
{
List<object> animals = new List<object>();
animals.Add(new Cat() { Name = "Pinky" });
animals.Add(new Cat() { Name = "Winky" });
animals.Add(new Dog() { Name = "Max" });
// Convert every item in the list into a dictionary
for (int i = 0; i < animals.Count; i++)
{
var animal = new Dictionary<string, object>();
animal.Add(animals[i].GetType().Name, animals[i]);
animals[i] = animal;
}
var serializer = new JavaScriptSerializer();
var json = serializer.Serialize(animals.ToArray());
animals = (List<object>)serializer.Deserialize(json, animals.GetType());
// convert every item in the dictionary back into a list<object> item
for (int i = 0; i < animals.Count; i++)
{
var animal = (Dictionary<string, object>)animals[i];
animal = (Dictionary<string, object>)animal.Values.First();
animals[i] = animal.Values.First();
}
}
2)或使用JavaScriptConverter也能够处理该序列化的类型。
[{ “猫”:{ “杂食”:真}},{ “土豚”:{ “食虫”:假}},{ “土豚”:{ “食虫”:真}}]
我得到这个工作按的问题。不完全直截了当,但这里就是这样。在Json.NET中没有简单的方法来做到这一点。如果它支持可以插入自己的类型信息的预序列化回调,那将非常棒,但那是另一回事。
我有一个多态类实现的接口(IShape)。其中一个类是一个容器(复合模式),并包含一个包含对象的列表。我用接口做了这个,但是相同的概念适用于基类。
public class Container : IShape
{
public virtual List<IShape> contents {get;set;}
// implement interface methods
按的问题,我想这是连载:
"container": {
"contents": [
{"box": { "TopLeft": {"X": 0.0,"Y": 0.0},"BottomRight": {"X": 1.0, "Y": 1.0} } },
{"line": {"Start": { "X": 0.0,"Y": 0.0},"End": {"X": 1.0,"Y": 1.0 }} },
等
要做到这一点,我写了一个包装类。每个实现接口的对象在包装器中都有一个属性。这将在序列化程序中设置属性名称。有条件的序列化确保使用正确的属性。所有接口方法都委派给包装类,并且访问者Accept()调用被定向到包装类。这意味着在使用接口的上下文中,Wrapped或unwrapped类将表现相同。
public class SerializationWrapper : IShape
{
[JsonIgnore]
public IShape Wrapped { get; set; }
// Accept method for the visitor - redirect visitor to the wrapped class
// so visitors will behave the same with wrapped or unwrapped.
public void Accept(IVisitor visitor) => Wrapped.Accept(visitor);
public bool ShouldSerializeline() => line != null;
// will serialize as line : { ...
public Line line { get =>Wrapped as Line;}
public bool ShouldSerializebox() => box != null;
public Box box { get => Wrapped as Box; }
public bool ShouldSerializecontainer() => container != null;
public Container container { get => Wrapped as Container; }
// IShape methods delegated to Wrapped
[JsonIgnore]
public Guid Id { get => Wrapped.Id; set => Wrapped.Id = value; }
我也有一个访问者模式实现来遍历对象图。我已经得到了这个软件设计的其余部分,但是如果你只有一个简单的集合,你可以迭代集合并添加包装器。
public class SerializationVisitor : IVisitor
{
public void Visit(IContainer shape)
{
// replace list items with wrapped list items
var wrappedContents = new List<IShape>();
shape.Contents.ForEach(s => { wrappedContents.Add(new SerializationWrapper(){ Wrapped = s}); s.Accept(this); });
shape.Contents = wrappedContents;
}
public void Visit(ILine shape){}
public void Visit(IBox shape){}
}
访问者用包装版本的类替换容器类的内容。
序列化,它会产生所需的输出。
SerializationVisitor s = new SerializationVisitor();
s.Visit(label);
因为我已经有游客和我通过接口尽一切可能只是简单做我自己的序列,反正.......
嘿,你找到一个解决方案这我目前面临着类似的问题?服务器(Java,带有Jersey的Glassfish)将对象序列化为JSON,客户端(C#)需要反序列化这个对象。当使用XML一切工作正常... – hage 2012-03-09 13:24:04