2011-03-03 48 views
15

使用.NET標準的JavascriptSerializer/JsonDataContractSerializer或外部解析器可以使用包裝器方法(包括對象類型)序列化對象數組嗎?具有多態對象的數組的JSON序列化

例如,從列表生成此JSON:

[{ 'dog': { ...dog properties... } }, 
{ 'cat': { ...cat properties... } }] 

代替典型:

[{ ...dog properties... }, 
{ ...cat properties... }] 

這與使用JsonTypeInfo.As.WRAPPER_OBJECT屬性傑克遜在Java中是可行的。

+0

嘿,你找到一個解決方案這我目前面臨着類似的問題?服務器(Java,帶有Jersey的Glassfish)將對象序列化爲JSON,客戶端(C#)需要反序列化這個對象。當使用XML一切工作正常... – hage 2012-03-09 13:24:04

回答

10

也許我看到的最接近的是使用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)。

我希望這有助於!

+0

謝謝你的詳細答案(我會upvote)。我意識到這個解決方案,但希望與在Java中實現的客戶端互操作,但無法接收這些「_​​_type」屬性。 – ggarber 2011-03-04 08:48:04

+0

非常感謝你,這正是我一直在尋找的! – 2013-02-15 14:25:48

+0

@PeterMorris - 不客氣。我最終也寫了一篇關於它的博客文章。這是一個有趣的問題要解決。 http://geekswithblogs.net/DavidHoerster/archive/2012/01/06/json.net-and-deserializing-anonymous-types.aspx – 2013-02-15 14:50:29

21

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中使用這個,但是它是非標準的,因爲它是非標準的。在這種情況下,您希望避免多態,並在消息中使版本和類型信息具有非常明確的屬性。

+2

可以創建一個SerializationBinder派生類,它可以爲類型映射更友好的字符串。這然後綁定到SerializerSettings.Binder屬性。這還沒有我想要的那麼幹淨。如果有一個屬性可以添加到類中以指定類型代碼應該是什麼,那將是非常好的。 – 2013-12-11 15:21:20

0

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也能夠處理該序列化的類型。

[{ 「貓」:{ 「雜食」:真}},{ 「土豚」:{ 「食蟲」:假}},{ 「土豚」:{ 「食蟲」:真}}]

​​
0

我得到這個工作按的問題。不完全直截了當,但這裏就是這樣。在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); 

因爲我已經有遊客和我通​​過接口盡一切可能只是簡單做我自己的序列,反正.......

相關問題