2016-02-10 12 views
6

私はいくつかの継承があります。逆シリアル化のためにカスタムJsonConverterが必要です。私はここでは、特定のプロパティの存在に基づいてタイプを決定する非常に簡単なアプローチを使用しています。コンバータをDeserializeObjectに渡さずに抽象アイテムのリストを逆シリアル化する方法

重要な注意:実際コードで、私はDeserializeObject通話に触れることができない、すなわち、私はそこにカスタム変換器を追加することはできません。私はこれがある程度XY問題であることを知っており、そのような私の答えは、私が望むものは不可能であるかもしれないことを認識しています。限り、これは私の質問はthis questionから少し異なることがわかります。ここで

は、私の状況のREPROです:

abstract class Mammal { } 
class Cat : Mammal { public int Lives { get; set; } } 
class Dog : Mammal { public bool Drools { get; set; } } 
class Person 
{ 
    [JsonConverter(typeof(PetConverter))] 
    public Mammal FavoritePet { get; set; } 

    [JsonConverter(typeof(PetConverter))] 
    public List<Mammal> OtherPets { get; set; } 
} 

そして、これは、カスタムコンバータです:

public class PetConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) { return objectType == typeof(Mammal); } 
    public override bool CanWrite { get { return false; } } 
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     if (reader.TokenType == JsonToken.Null) return null; 
     JObject jsonObject = JObject.Load(reader); 
     if (jsonObject["Lives"] != null) return jsonObject.ToObject<Cat>(serializer); 
     if (jsonObject["Drools"] != null) return jsonObject.ToObject<Dog>(serializer); 
     return null; 
    } 
} 

これはFavoritePetのため正常に動作しますが、OtherPetsのために多くはない、それはリストだから。

Newtonsoft.Json.JsonReaderException:エラーがJsonReaderからjオブジェクトを読み込むここでNUnitのテストと私の問題を再現する方法です:ので

[TestFixture] 
class MyTests 
{ 
    [Test] 
    public void CanSerializeAndDeserializeSingleItem() 
    { 
     var person = new Person { FavoritePet = new Cat { Lives = 9 } }; 
     var json = JsonConvert.SerializeObject(person); 
     var actual = JsonConvert.DeserializeObject<Person>(json); 
     Assert.That(actual.FavoritePet, Is.InstanceOf<Cat>()); 
    } 

    [Test] 
    public void CanSerializeAndDeserializeList() 
    { 
     var person = new Person { OtherPets = new List<Mammal> { new Cat { Lives = 9 } } }; 
     var json = JsonConvert.SerializeObject(person); 
     var actual = JsonConvert.DeserializeObject<Person>(json); 
     Assert.That(actual.OtherPets.Single(), Is.InstanceOf<Cat>()); 
    } 
} 

後者の試験は赤です。現在のJsonReaderアイテムはオブジェクトではありません:StartArray。パス「OtherPets」、行1、位置33

私も、その結果OtherPetsのカスタムコンバータ、なしで試してみた:

Newtonsoft.Json.JsonSerializationException:インスタンスを作成できませんでした。タイプJsonConverterLists.Mammalです。型はインタフェースまたは抽象クラスであり、インスタンス化できません。

var actual = JsonConvert.DeserializeObject<Person>(json, new PetConverter()); 

しかし、繰り返し:パス 'OtherPets [0] .Lives'、行1、位置42

私は何が起こっている理解し、私も、私はでそれを修正することができることを知っています上の注:DeserializeObject呼び出しを変更することはできません。これは、現在変更できないライブラリ内の関数内にラップされているためです。

アトリビュートベースアプローチで同じことを実行する方法はありますか。各エントリがカスタムコンバータを取るリスト用のビルトインコンバータがありますか?それとも私自身の、別のコンバータをロールバックする必要がありますか?


脚注、再現する方法:

  • のVisual Studio 2013 =>新鮮な新しい.NET 4.5。1クラスライブラリ
  • Install-Package Newtonsoft.Json -Version 7.0.1
  • Install-Package nunit -Version 2.6.4

あなたは自分の新鮮な名前空間内に上記の3つのコードブロックをドロップ2つ目は失敗見て、NUnitのテストを実行することができます。

答えて

0

、あなたは同じコンバータにリストの責任を追加したい場合は、あなたがテストに合格するためにこれを行うことができます。このようUpvoted

public class PetConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) { return objectType == typeof(Mammal); } 
    public override bool CanWrite { get { return false; } } 
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     if (reader.TokenType == JsonToken.Null) 
     { 
      return null; 
     } 

     if (reader.TokenType == JsonToken.StartArray) 
     { 
      return JArray.Load(reader) 
       .Cast<JObject>() 
       .Select(o => JObjectToMammal(o, serializer)) 
       .ToList(); 
     } 

     return JObjectToMammal(JObject.Load(reader), serializer); 
    } 

    private Mammal JObjectToMammal(JObject jsonObject, JsonSerializer serializer) 
    { 
     if (jsonObject["Lives"] != null) return jsonObject.ToObject<Cat>(serializer); 
     if (jsonObject["Drools"] != null) return jsonObject.ToObject<Dog>(serializer); 
     return null; 
    } 
} 
+0

あなたのコードは有望そうですが、Stackoverflow例外をスローしています...それはjsonObject.ToObjectはReadJsonを呼び出すようです – Bidou

+0

@Bidou奇妙な、奇妙な。私が覚えている限り、私はこの解決策で*のような*のコードを使用していました。単体テストは正常に動作しました。あなたのレプロも*まったく同じですか、微妙に違いますか?どのバージョンのNewtonSoftを使いましたか? – Jeroen

+0

Mmhmhm私は、 'DefaultContractResolver'から派生した新しいクラスを作成することで動作させることができました。これは、この奇妙な行動を防ぐ... – Bidou

1

少し変換されたクラスを調整しました。それは良いことだ願っています -

AmitKumarGhoshの提案@オフに基づいて
public class PetConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) { return objectType == typeof(Mammal); } 
    public override bool CanWrite { get { return false; } } 
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     if (reader.TokenType == JsonToken.Null) return null; 

     if (reader.TokenType == JsonToken.StartArray) 
     { 
      var li = new List<Mammal>(); 
      var arr = JArray.Load(reader); 
      foreach (JObject obj in arr) 
      { 
       if (obj["Drools"] != null) 
       { 
        var k = obj.ToObject<Dog>(serializer); 
        li.Add(k); 
       } 
      } 

      return li; 
     } 


     JObject jsonObject = JObject.Load(reader); 
     if (jsonObject["Lives"] != null) return jsonObject.ToObject<Cat>(serializer); 
     //if (jsonObject["Drools"] != null) return jsonObject.ToObject<Dog>(serializer); 
     return null; 
    } 
} 
+0

[作業中の回答](http://stackoverflow.com/a/35310669/419956)につながった。テストに合格しないので、そのまま受け入れることはできません。それでもまだバグが2つあります。 – Jeroen

関連する問題