2016-04-12 20 views
1

私はRowオブジェクトのリストを含むCabinクラスを持っています。私はこのようなオブジェクトをシリアル化したいと思いますが、デシリアライズするときにRowオブジェクトをRowオブジェクトから継承するRowRuleオブジェクトにしたいと思います。以下は、私が試してきたサンプルコードです。jsonオブジェクトを特定のサブクラスに逆シリアル化する方法

class Program 
{ 
    static void Main(string[] args) 
    { 
     var cabin = new Cabin(); 
     var row = new Row(); 
     row.Status = "Success"; 
     cabin.Rows = new List<Row>() 
     { 
      row, 
      row 
     }; 

     JsonSerializerSettings settings = new JsonSerializerSettings() 
     { 
      TypeNameHandling = TypeNameHandling.Auto 
     }; 
     string json = JsonConvert.SerializeObject(cabin, Formatting.Indented, settings); 
     Console.WriteLine(json); 

     Cabin obj = JsonConvert.DeserializeObject<Cabin>(json, 
      new JsonSerializerSettings() {TypeNameHandling = TypeNameHandling.Auto}); 
     Console.WriteLine(obj); 
     Debug.Assert(obj.Rows.First().GetType().Name == "RowRule"); 
    } 
} 

class Cabin 
{ 
    public IList<Row> Rows { get; set; } 
} 

class Row 
{ 
    public string Status { get; set; } 
} 

class RowRule : Row 
{ 

} 
+0

なぜあなたは 'TypeNameHandling.Auto'を使用していますか? 'Row'の他のサブクラスがリストに存在する可能性はありますか?特に指定されていない場合は' Row'から 'RowRule'に変換したいだけですか? – dbc

+0

関連:[Json.netでカスタムJsonConverterとTypeNameHandlingを使用する](https://stackoverflow.com/q/29810004/3744182) – dbc

答えて

2

簡単な答えはCustomCreationConverter<Row>を使用して返すことですRowRuleCreate()から:

は、あなたがこのようにそれをやってみたかったかもしれませただし

class RowToRoleRuleConverter : CustomCreationConverter<Row> 
{ 
    public override Row Create(Type objectType) 
    { 
     if (objectType.IsAssignableFrom(typeof(RowRule))) 
      return Activator.CreateInstance<RowRule>(); 
     return (Row)Activator.CreateInstance(objectType); 
    } 
} 

、使用しているTypeNameHandling.AutoましたJSONに多型の"$type"プロパティが存在する可能性があることを意味します。残念ながら、CustomCreationConverter<T>はこれらのプロパティを無視します。したがって、いくつかの追加作業を行うとDowncastingConverter<TBase, TDerived>を作成する必要があります:

public class DowncastingConverter<TBase, TDerived> : PolymorphicCreationConverter<TBase> where TDerived : TBase 
{ 
    protected override TBase Create(Type objectType, Type polymorphicType, object existingValue, IContractResolver contractResolver, JObject obj) 
    { 
     Type createType = objectType; 
     if (createType.IsAssignableFrom(polymorphicType)) 
      createType = polymorphicType; 
     if (createType.IsAssignableFrom(typeof(TDerived))) 
      createType = typeof(TDerived); 

     if (existingValue != null && createType.IsAssignableFrom(existingValue.GetType())) 
      return (TBase)existingValue; 

     var contract = contractResolver.ResolveContract(createType); 
     return (TBase)contract.DefaultCreator(); 
    } 
} 

public abstract class PolymorphicCreationConverter<T> : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return typeof(T).IsAssignableFrom(objectType); 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     throw new NotSupportedException("CustomCreationConverter should only be used while deserializing."); 
    } 

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

     var polymorphicTypeString = (string)obj["$type"]; 
     if (polymorphicTypeString != null) 
     { 
      if (serializer.TypeNameHandling != TypeNameHandling.None) 
      { 
       string typeName, assemblyName; 
       ReflectionUtils.SplitFullyQualifiedTypeName(polymorphicTypeString, out typeName, out assemblyName); 
       polymorphicType = serializer.Binder.BindToType(assemblyName, typeName); 
      } 
      obj.Remove("$type"); 
     } 

     var value = Create(objectType, polymorphicType, existingValue, serializer.ContractResolver, obj); 
     if (value == null) 
      throw new JsonSerializationException("No object created."); 

     using (var subReader = obj.CreateReader()) 
      serializer.Populate(subReader, value); 
     return value; 
    } 

    protected abstract T Create(Type objectType, Type polymorphicType, object existingValue, IContractResolver iContractResolver, JObject obj); 

    public override bool CanWrite { get { return false; } } 
} 

internal static class ReflectionUtils 
{ 
    // Utilities taken from https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs 
    // I couldn't find a way to access these directly. 

    public static void SplitFullyQualifiedTypeName(string fullyQualifiedTypeName, out string typeName, out string assemblyName) 
    { 
     int? assemblyDelimiterIndex = GetAssemblyDelimiterIndex(fullyQualifiedTypeName); 

     if (assemblyDelimiterIndex != null) 
     { 
      typeName = fullyQualifiedTypeName.Substring(0, assemblyDelimiterIndex.GetValueOrDefault()).Trim(); 
      assemblyName = fullyQualifiedTypeName.Substring(assemblyDelimiterIndex.GetValueOrDefault() + 1, fullyQualifiedTypeName.Length - assemblyDelimiterIndex.GetValueOrDefault() - 1).Trim(); 
     } 
     else 
     { 
      typeName = fullyQualifiedTypeName; 
      assemblyName = null; 
     } 
    } 

    private static int? GetAssemblyDelimiterIndex(string fullyQualifiedTypeName) 
    { 
     int scope = 0; 
     for (int i = 0; i < fullyQualifiedTypeName.Length; i++) 
     { 
      char current = fullyQualifiedTypeName[i]; 
      switch (current) 
      { 
       case '[': 
        scope++; 
        break; 
       case ']': 
        scope--; 
        break; 
       case ',': 
        if (scope == 0) 
        { 
         return i; 
        } 
        break; 
      } 
     } 

     return null; 
    } 
} 

次にようにそれを使用します。

JsonSerializerSettings readSettings = new JsonSerializerSettings() 
{ 
    TypeNameHandling = TypeNameHandling.Auto, 
    Converters = new[] { new DowncastingConverter<Row, RowRule>() }, 
}; 
Cabin obj = JsonConvert.DeserializeObject<Cabin>(json, readSettings); 

プロトタイプfiddle

+0

申し訳ありませんが、これは素晴らしいです。 Json.Netの一部である必要があります。 – JHo

0

あなたのサンプルコードの問題は、あなたがのオブジェクトを作成し、可能ではないRowRuleを取得しようとしている、です。

 var cabin = new Cabin(); 
     var row = new RowRule(); // create derived object 
     row.Status = "Success"; 
     cabin.Rows = new List<Row>() 
     { 
      row, 
      row 
     }; 
+0

両方のケースで生成されたJsonが同一である限り、可能です。 Newtonsoftはデフォルトでjsonの型情報を入力しません。 – Dan

+0

'TypeNameHandling = TypeNameHandling.Auto'では、型名が含まれます。 http://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_TypeNameHandling.htm – vendettamit

+0

を参照してください。ただし、json文字列を編集して削除することはできますか? – Dan

関連する問題