2017-10-23 17 views
3

実行時に特別なSystemComponent.PropertyDescriptor-sを持つオブジェクトに「プロパティ」を追加する機能を実装しました。ランタイムをシリアル化する方法Jsonに「プロパティ」を追加

これらのプロパティはReflectionではなくComponentModel.TypeDescriptorでのみアクセス可能であるため、プロパティはWPF環境では有効ですが、シリアル化ではうまく機能しません。

これは、私が知っているすべてのJSONシリアライザがそのタイプで反射を使用しているためです。私はNewtonsoft.Json、System.Json、System.Web.Script.JavaScriptSerializer、System.Runtime.Serialization.Jsonを分析しました。

これらのシリアライザのいずれもインスタンスのプロパティの取得を変更することができないため、これらのシリアライザを使用することはできません(ContractResolverは不可能です)。

これらのシリアライザのいずれかでJSONシリアライズを行う方法はありますか?特別な設定で、シリアライザなどの特定のメソッドをオーバーライドすることがありますか? この要件を満たす別のシリアライザがありますか?

を背景:

実行時プロパティのアイデアはthis blog entryに基づいています。

逐次化の要件は、viewModelをシリアル化してクライアントに送信するdotNetifyの使用にあります。

現在、dotnetifyのフォークを作成し、部分的にNewtonsoft.Jsonと再帰ヘルパーを使用してシリアライズするための一時的な回避策を作成しました。 (もしあなたがそれに興味があるなら、あなたはdiffを見ることができます:the Fork)。

+0

1)に依存せずに、より一般的なアプローチであるので、私は、これを掲載しています。 ContractResolverできません* - カスタムJson.NETコントラクトリゾルバが動作しなかった理由はわかりません。あなたは間違いなく['CreateProperties()'](https://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver_CreateProperties.htm)をオーバーライドし、カスタム合成プロパティを追加することができます。 2)コンソールアプリケーションなどのテスト目的で使用できるカスタムプロパティの最小限の実装を指摘できますか? – dbc

+0

@dbcプロパティは、TypeDescriptorを介してオブジェクト上でしかアクセスできません。タイプからそれらを得ることはできません。後でサンプルの実装を提供します。 CreateProperties(Objectオブジェクト、MemberSerialization memberSerialization)が存在しないことも悪いことです。レゾルバ内のすべてがタイプにリレーされます。 –

+0

@dbcここではDynamicProperties(最小クラス、私はこれをRx Stuffでもっとビルドしました)のGithubリポジトリ(https://github.com/KeilFelix/DynamicProperties)とシリアル化を試してみるConsoleappです –

答えて

1

一つの可能​​性は、タイプTTargetの特定のオブジェクトをシリアル化、custom ContractResolverを作成することで、指定されたターゲットのために、その対応するDynamicPropertyManager<TTarget>で指定されたプロパティのIEnumerable<KeyValuePair<Object, Object>>を返す合成​​を加算します。

まず、次のように契約リゾルバを定義します。

public class DynamicPropertyContractResolver<TTarget> : DefaultContractResolver 
{ 
    readonly DynamicPropertyManager<TTarget> manager; 
    readonly TTarget target; 

    public DynamicPropertyContractResolver(DynamicPropertyManager<TTarget> manager, TTarget target) 
    { 
     if (manager == null) 
      throw new ArgumentNullException(); 
     this.manager = manager; 
     this.target = target; 
    } 

    protected override JsonObjectContract CreateObjectContract(Type objectType) 
    { 
     var contract = base.CreateObjectContract(objectType); 

     if (objectType == typeof(TTarget)) 
     { 
      if (contract.ExtensionDataGetter != null || contract.ExtensionDataSetter != null) 
       throw new JsonSerializationException(string.Format("Type {0} already has extension data.", typeof(TTarget))); 
      contract.ExtensionDataGetter = (o) => 
       { 
        if (o == (object)target) 
        { 
         return manager.Properties.Select(p => new KeyValuePair<object, object>(p.Name, p.GetValue(o))); 
        } 
        return null; 
       }; 
      contract.ExtensionDataSetter = (o, key, value) => 
       { 
        if (o == (object)target) 
        { 
         var property = manager.Properties.Where(p => string.Equals(p.Name, key, StringComparison.OrdinalIgnoreCase)).SingleOrDefault(); 
         if (property != null) 
         { 
          if (value == null || value.GetType() == property.PropertyType) 
           property.SetValue(o, value); 
          else 
          { 
           var serializer = JsonSerializer.CreateDefault(new JsonSerializerSettings { ContractResolver = this }); 
           property.SetValue(o, JToken.FromObject(value, serializer).ToObject(property.PropertyType, serializer)); 
          } 
         } 
        } 
       }; 
      contract.ExtensionDataValueType = typeof(object); 
     } 

     return contract; 
    } 
} 

を次に、以下のように、あなたのオブジェクトをシリアル化:必要に応じて出力、

var obj = new object(); 

//Add prop to instance 
int propVal = 0; 
var propManager = new DynamicPropertyManager<object>(obj); 
propManager.Properties.Add(
    DynamicPropertyManager<object>.CreateProperty<object, int>(
    "Value", t => propVal, (t, y) => propVal = y, null)); 

propVal = 3; 

var settings = new JsonSerializerSettings 
{ 
    ContractResolver = new DynamicPropertyContractResolver<object>(propManager, obj), 
}; 

//Serialize object here 
var json = JsonConvert.SerializeObject(obj, Formatting.Indented, settings); 

Console.WriteLine(json); 

{"Value":3} 

を明らかにこれは可能性がありコレクションを渡すことによって動的プロパティを持つオブジェクトのグラフを直列化することに拡張することができます動的プロパティマネージャとターゲットのうち、拡張されたものはDynamicPropertyContractResolver<TTarget>です。コントラクトリゾルバがターゲットから(シリアライズされた)ターゲットからDynamicPropertyManagerにマッピングするためのメカニズムを持っている限り、合成を作成する基本的な考え方は、ExtensionDataGetter(およびデシリアル化の場合はExtensionDataSetter)です。

制限:TTargetタイプのメンバーがすでにextension dataのメンバーである場合、これは機能しません。

+0

素晴らしい、ありがとうございました!これは正しい方向に私を得た。 ExtensionDataGetterとSetterにオブジェクトのスコープが再度あります! :)私は、System.ComponentModel.TypeDescriptorを使用するバージョンを作成し、DynamicPropertiesに依存しません。それはレポにコミットされます。 –

1

私の解決策は、System.ComponentModelを使用するContractResolverです。TypeDescriptor

public class TypeDescriptorContractResolver : DefaultContractResolver 
{ 

    public TypeDescriptorContractResolver() 
    { 
    } 

    protected override JsonObjectContract CreateObjectContract(Type objectType) 
    { 
     var contract = base.CreateObjectContract(objectType); 


     if (contract.ExtensionDataGetter != null || contract.ExtensionDataSetter != null) 
      throw new JsonSerializationException(string.Format("Type {0} already has extension data.", objectType)); 

     contract.ExtensionDataGetter = (o) => 
     { 
      return TypeDescriptor.GetProperties(o).OfType<PropertyDescriptor>().Select(p => new KeyValuePair<object, object>(p.Name, p.GetValue(o))); 
     }; 

     contract.ExtensionDataSetter = (o, key, value) => 
     { 
      var property = TypeDescriptor.GetProperties(o).OfType<PropertyDescriptor>().Where(p => string.Equals(p.Name, key, StringComparison.OrdinalIgnoreCase)).SingleOrDefault(); 
      if (property != null) 
      { 
       if (value == null || value.GetType() == property.PropertyType) 
        property.SetValue(o, value); 
       else 
       { 
        var serializer = JsonSerializer.CreateDefault(new JsonSerializerSettings { ContractResolver = this }); 
        property.SetValue(o, JToken.FromObject(value, serializer).ToObject(property.PropertyType, serializer)); 
       } 
      } 
     }; 
     contract.ExtensionDataValueType = typeof(object); 

     return contract; 
    } 
} 

それは例えば*のDynamicProperties

関連する問題