6

次のクラスを検討してください。

public class User
{
  public virtual int Id {get;set;}
  public virtual string Name {get;set;}
  public virtual User Superior {get;set;}
}

私の目標は、次のように newtonsofts json.net を使用してこれを json としてシリアル化することです。

{
  Id: 101,
  Name: 'Mithon',
  SuperiorId: 100,
  SuperiorName: 'TheMan'
}

なぜ私はこれをしたいのですか?動的オブジェクトの中間層を生成せずに、Json を DTO として使用したいからです。DTO の生成は、明示的にではなく、規則によって動的に行う必要があります。これに強く反対する人もいるかもしれませんが、私のアプローチについて議論することは重要ではありません。それができるかどうか、そしてどのようにできるかを知りたいだけです。

課題はJsonPropertyAttribute、優れたプロパティを使用すると、出力として 1 つのプロパティしか生成されないことです。ここでは 2 つ必要です。を使用するJsonObjectAttributeと、ネストされた属性が取得され、最上位レベルのユーザーもフラット化されるという問題が発生します。

幸いなことに、json.net ライブラリには、目的の結果を得るために何かを拡張できる十分な保護および/またはパブリック プロパティとメソッドがあるようです。問題は、目的の場所に到達するには、どのクラスとメソッドから始めるべきかということです。DefaultContractResolver から派生し、GetProperties メソッドをオーバーライドするのは良い場所でしょうか、それとも他の場所を探す必要がありますか?

4

1 に答える 1

2

手短に言えば、はい、それが出発点として適切な場所です。これが私が最終的に得たものです(今のところ):

public class MyContractResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var properties = base.CreateProperties(type, memberSerialization);

        foreach (var pi in type.GetProperties().Where(pi => typeof (Entity).IsAssignableFrom(pi.DeclaringType) && Attribute.IsDefined((MemberInfo) pi, typeof(IdNameMemberAttribute))))
        {
            properties.Add(CreateReferenceProperty(pi, Reflect.GetProperty<Entity>(e => e.Id)));
            properties.Add(CreateReferenceProperty(pi, Reflect.GetProperty<Entity>(e => e.Name)));
        }

        return properties;
    }

    private JsonProperty CreateReferenceProperty(PropertyInfo reference, PropertyInfo referenceMember)
    {
        var jsonProperty = base.CreateProperty(reference, MemberSerialization.OptOut);
        jsonProperty.PropertyName += referenceMember.Name;
        jsonProperty.ValueProvider = new ReferencedValueProvider(reference, referenceMember);
        jsonProperty.Writable = false;

        return jsonProperty;
    }
}

IdNameMemberAttributeシリアル化する参照プロパティに注釈を付けるために使用する単なる空の属性です。重要な点は、Json.NET が認識して JsonProperty を生成するために使用するもので注釈を付けないことです。そうすれば、CreateProperties から JsonProperty が重複することはありません。

または、 DataMemberAttribute から派生させ、 JsonProperty を検索、変更、および複製して、 my Idandを表すこともできNameます。

私のasp.net Web APIでは、このMyContractResolverをJsonFormatter.SerializerSettingsのContractResolverとして設定します。

それで、シリアル化のために私を修正しました。逆シリアル化のために、PropertyInfo とオブジェクトを格納するカスタム ChangeSet オブジェクトがあります。次に、逆シリアル化中に Id を保持し、後でデータ ストアから ID を解決します。私の場合は、データ ストア セッションにアクセスするカスタム ActionFilter を使用します。

これが私の連載の本質です。

var jsonSource = streamReader.ReadToEnd();
var deserializedObject = JsonConvert.DeserializeObject(jsonSource, type, SerializerSettings);
var changeSet = deserializedObject as PropertyChangeSet;
if (changeSet != null)
{
    var jsonChange = JObject.Parse(jsonSource)["Change"].Cast<JProperty>().ToArray();

    IDictionary<string, int> references = jsonChange.Where(IsReferenceId).ToDictionary(t => t.Name.Substring(0, t.Name.Length - 2), t => t.Value.ToObject<int>());
    changeSet.References = references;

    var properties = jsonChange.Where(jp => !IsReferenceId(jp)).Select(t => t.Name).ToList();
    changeSet.Primitives = properties;
}

さしあたって、クリーン エンティティと動的シリアライゼーションのすべての悲惨な詳細は悲しいことに 2 つの場所にカプセル化されていますが、シリアライザーからデータ ソースにアクセスしたくないので、仕方がありませんでした。

于 2013-03-13T15:26:49.327 に答える