web-dev-qa-db-ja.com

IEnumerable <ISomeInterface>型のプロパティを持つNewtonSoft.Json SerializeおよびDeserializeクラス

SOAP Xmlの代わりにASP.NET MVC Web APIで生成されたJsonデータを使用するようにコードを移動しようとしています。

型のプロパティのシリアル化と逆シリアル化で問題が発生しました。

IEnumerable<ISomeInterface>.

以下に簡単な例を示します。

public interface ISample{
  int SampleId { get; set; }
}
public class Sample : ISample{
  public int SampleId { get; set; }
}
public class SampleGroup{
  public int GroupId { get; set; }
  public IEnumerable<ISample> Samples { get; set; }
 }
}

SampleGroupのインスタンスを次のように簡単にシリアル化できます。

var sz = JsonConvert.SerializeObject( sampleGroupInstance );

ただし、対応する逆シリアル化は失敗します。

JsonConvert.DeserializeObject<SampleGroup>( sz );

この例外メッセージ:

「タイプJsonSerializationExample.ISampleのインスタンスを作成できませんでした。タイプはインターフェースまたは抽象クラスであり、インスタンス化できません。」

JsonConverterを派生させると、次のようにプロパティを装飾できます。

[JsonConverter( typeof (SamplesJsonConverter) )]
public IEnumerable<ISample> Samples { get; set; }

JsonConverterは次のとおりです。

public class SamplesJsonConverter : JsonConverter{
  public override bool CanConvert( Type objectType ){
    return ( objectType == typeof (IEnumerable<ISample>) );
  }

  public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer ){
    var jA = JArray.Load( reader );
    return jA.Select( jl => serializer.Deserialize<Sample>( new JTokenReader( jl ) ) ).Cast<ISample>( ).ToList( );
  }

  public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer ){
    ... What works here?
  }
}

このコンバーターは逆シリアル化の問題を解決しますが、WriteJsonメソッドをコーディングしてシリアル化を再び機能させる方法を理解することはできません。

誰でも支援できますか?

これはそもそも問題を解決する「正しい」方法ですか?

62
AndyDBell

JsonConverterAttributeを使用する必要はなく、モデルをクリーンに保ち、CustomCreationConverterを使用する必要もありません。コードは簡単です。

public class SampleConverter : CustomCreationConverter<ISample>
{
    public override ISample Create(Type objectType)
    {
        return new Sample();
    }
}

次に:

var sz = JsonConvert.SerializeObject( sampleGroupInstance );
JsonConvert.DeserializeObject<SampleGroup>( sz, new SampleConverter());

ドキュメント: CustomCreationConverter でデシリアライズ

65
cuongle

Json.netによって提供される、非常にシンプルですぐに使用できるサポートです。シリアライズおよびデシリアライズ中に次のJsonSettingsを使用するだけです。

JsonConvert.SerializeObject(graph,Formatting.None, new JsonSerializerSettings()
{
    TypeNameHandling =TypeNameHandling.Objects,
    TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple
});

また、デシリアライズには次のコードを使用します。

JsonConvert.DeserializeObject(Encoding.UTF8.GetString(bData),type,
    new JsonSerializerSettings(){TypeNameHandling = TypeNameHandling.Objects}
);

あなたにとって重要なJsonSerializerSettingsオブジェクト初期化子に注意してください。

18
Sunil S

JsonSerializerSettingsに特別な設定-TypeNameHandling.Allと呼ばれる特別な設定を使用して、この問題を解決しました。

TypeNameHandling設定には、JSONのシリアル化時に型情報が含まれ、JSONの逆シリアル化時に作成型が作成されるように型情報を読み取ります

シリアル化:

var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
var text = JsonConvert.SerializeObject(configuration, settings);

デシリアライゼーション:

var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
var configuration = JsonConvert.DeserializeObject<YourClass>(json, settings);

クラスYourClassには任意の種類の基本型フィールドが含まれている可能性があり、適切にシリアル化されます。

11
adam.bielasty

素晴らしい解決策、ありがとう! AndyDBellの質問とCuong Leの答えを取り上げて、2つの異なるインターフェイスの実装を使用した例を作成しました。

public interface ISample
{
    int SampleId { get; set; }
}

public class Sample1 : ISample
{
    public int SampleId { get; set; }
    public Sample1() { }
}


public class Sample2 : ISample
{
    public int SampleId { get; set; }
    public String SampleName { get; set; }
    public Sample2() { }
}

public class SampleGroup
{
    public int GroupId { get; set; }
    public IEnumerable<ISample> Samples { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        //Sample1 instance
        var sz = "{\"GroupId\":1,\"Samples\":[{\"SampleId\":1},{\"SampleId\":2}]}";
        var j = JsonConvert.DeserializeObject<SampleGroup>(sz, new SampleConverter<Sample1>());
        foreach (var item in j.Samples)
        {
            Console.WriteLine("id:{0}", item.SampleId);
        }
        //Sample2 instance
        var sz2 = "{\"GroupId\":1,\"Samples\":[{\"SampleId\":1, \"SampleName\":\"Test1\"},{\"SampleId\":2, \"SampleName\":\"Test2\"}]}";
        var j2 = JsonConvert.DeserializeObject<SampleGroup>(sz2, new SampleConverter<Sample2>());
        //Print to show that the unboxing to Sample2 preserved the SampleName's values
        foreach (var item in j2.Samples)
        {
            Console.WriteLine("id:{0} name:{1}", item.SampleId, (item as Sample2).SampleName);
        }
        Console.ReadKey();
    }
}

SampleConverterの汎用バージョン:

public class SampleConverter<T> : CustomCreationConverter<ISample> where T: new ()
{
    public override ISample Create(Type objectType)
    {
        return ((ISample)new T());
    }
}
4
Daniel Melo

私のプロジェクトでは、このコードは常に、特別なコンバーターが存在しないかのように指定された値をシリアル化するデフォルトのシリアライザーとして機能しました。

serializer.Serialize(writer, value);
2
fero

私はこれを機能させました:

明示的な変換

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
                                    JsonSerializer serializer)
    {
        var jsonObj = serializer.Deserialize<List<SomeObject>>(reader);
        var conversion = jsonObj.ConvertAll((x) => x as ISomeObject);

        return conversion;
    }
1
dvr

それを持っている:

public interface ITerm
{
    string Name { get; }
}

public class Value : ITerm...

public class Variable : ITerm...

public class Query
{
   public IList<ITerm> Terms { get; }
...
}

私はそれを実装する変換トリックを管理しました:

public class TermConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var field = value.GetType().Name;
        writer.WriteStartObject();
        writer.WritePropertyName(field);
        writer.WriteValue((value as ITerm)?.Name);
        writer.WriteEndObject();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
        JsonSerializer serializer)
    {
        var jsonObject = JObject.Load(reader);
        var properties = jsonObject.Properties().ToList();
        var value = (string) properties[0].Value;
        return properties[0].Name.Equals("Value") ? (ITerm) new Value(value) : new Variable(value);
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof (ITerm) == objectType || typeof (Value) == objectType || typeof (Variable) == objectType;
    }
}

JSONで次のようにシリアライズおよびデシリアライズできます。

string JsonQuery = "{\"Terms\":[{\"Value\":\"This is \"},{\"Variable\":\"X\"},{\"Value\":\"!\"}]}";
...
var query = new Query(new Value("This is "), new Variable("X"), new Value("!"));
var serializeObject = JsonConvert.SerializeObject(query, new TermConverter());
Assert.AreEqual(JsonQuery, serializeObject);
...
var queryDeserialized = JsonConvert.DeserializeObject<Query>(JsonQuery, new TermConverter());
0