78

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 メソッドをコーディングする方法がわかりません。

誰でも手伝ってもらえますか?

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

4

8 に答える 8

73

を使用する必要はありません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 で逆シリアル化する

于 2012-08-01T14:31:39.530 に答える
20

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

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

Deserialzing には、以下のコードを使用します。

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

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

于 2014-01-14T09:57:14.430 に答える
15

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

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には、あらゆる種類の基本型フィールドが含まれている可能性があり、適切にシリアル化されます。

于 2016-11-02T15:11:04.280 に答える
1

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

明示的な変換

    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;
    }
于 2013-02-20T11:17:10.113 に答える
1

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

serializer.Serialize(writer, value);
于 2012-08-01T14:10:36.347 に答える
0

それを持っている:

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());
于 2016-02-21T17:20:34.450 に答える
0

ほとんどの場合、データ コントラクト全体に型を指定するのではなく、抽象、インターフェイス、またはそれらのリストを含むもののみを指定することを考慮してください。また、これらのインスタンスが非常にまれであり、データ エンティティ内で簡単に識別できることを考慮すると、最も簡単で冗長でない方法は次を使用することです

[JsonProperty(ItemTypeNameHandling = TypeNameHandling.Objects)]
public IEnumerable<ISomeInterface> Items { get; set; }

列挙可能/リスト/コレクションを含むプロパティの属性として。これはそのリストのみを対象とし、次のように、含まれているオブジェクトのタイプ情報のみを追加します。

{
  "Items": [
    {
      "$type": "Namespace.ClassA, Assembly",
      "Property": "Value"
    },
    {
      "$type": "Namespace.ClassB, Assembly",
      "Property": "Value",
      "Additional_ClassB_Property": 3
    }
  ]
}

いくつかのコンバーターに隠されるのではなく、データモデルの複雑さが導入された場所に、クリーンでシンプルで配置されています。

于 2020-09-02T09:36:55.973 に答える