2017-12-23 22 views
3

が、私はこのようなクラスがあるとします。Newtonsoft.JsonでC#refフィールドをシリアライズするには?

[JsonObject(MemberSerialization.OptIn)] 
public class TestClass 
{ 
    private readonly int _SomeField; 

    [JsonProperty(nameof(InputInfo))] 
    public ref readonly int SomeField => ref _SomeField; 
} 

:このクラスはフィールドがメンバーフィールドではないというのが私の現実の世界のシナリオでは、削ぎ落とした例であり、そうでない場合は、私がその上にJson属性が追加されました。そのフィールドは、クラスのメンバである別のオブジェクトによって公開されるフィールドです。パブリックプロパティは、そのフィールドに簡単にアクセスできるように、そのフィールドをユーザーに公開しています。また

は、実際のプロパティTIPEはないintが、12バイトstructので、私は値によって無用のコピーを避けるために、参照することにより、それを返しています。

私はJsonConvert.SerializeObject(this, Formatting.Indented)を使ってそのようなクラスをシリアライズしています。

Newtonsoft.Jsonはフィールド/プロパティ値にアクセスできないという値を変換するときに例外をスローします(refパラメータで、ライブラリのクラッシュによって使用されるリフレクションプロシージャになります)。

私はカスタムJsonConverterを試してみましたが、追加のコンバータを使用する前でもクラッシュが発生しました。

私は、簡単な解決策は、そのフィールドを値で、参照ではなく、単にJsonのシリアル化に使用するだけの副次的なプライベートパラメータを追加することだと知っています。可能であれば(役に立たないフィールド/プロパティを導入することなく)より良い解決策を探しています。

ありがとうございました!

答えて

2

これは長すぎてコメントにはなりません。誰かが別の回答を投稿した場合は削除します。クイックルックでは、現時点でそれを上書きすることはできません。

問題はライン110 DynamicValueProvider.csで発生します。

public object GetValue(object target) 
{ 
     try 
     { 
      if (_getter == null) 
      { 
       _getter = DynamicReflectionDelegateFactory.Instance.CreateGet<object>(_memberInfo); 
      } 

      return _getter(target); //Line 100 
     } 
     catch (Exception ex) 
     { 
      throw new JsonSerializationException("Error getting value from '{0}' on '{1}'.".FormatWith(CultureInfo.InvariantCulture, _memberInfo.Name, target.GetType()), ex); 
     } 
} 

原因はCreateGetであり、それは正しく、これらのタイプを処理するためのメソッドを生成することはできません。たぶんあなたはGitHub上に新しい問題をオープンするべきです(まだ存在しない場合)。私はこの1つの報告をお勧めしたいので、私はすべての既存の問題を見つけることができませんでした

using System; 
using System.Reflection; 
using System.Reflection.Emit; 

namespace ConsoleApp15 
{ 
    public class TestClass 
    { 
     public TestClass() 
     { 
      _SomeField = 42; 
     } 

     private readonly int _SomeField; 

     public ref readonly int SomeField => ref _SomeField; 
    } 

    internal class Program 
    { 
     private static void Main(string[] args) 
     { 
      var propertyInfo = typeof(TestClass).GetProperty("SomeField"); 
      var getMethod = CreateGet<object>(propertyInfo); 

      TestClass obj = new TestClass(); 

      var result = getMethod(obj); 
     } 

     public static Func<T, object> CreateGet<T>(PropertyInfo propertyInfo) 
     { 
      DynamicMethod dynamicMethod = CreateDynamicMethod("Get" + propertyInfo.Name, typeof(object), new[] { typeof(T) }, propertyInfo.DeclaringType); 
      ILGenerator generator = dynamicMethod.GetILGenerator(); 

      GenerateCreateGetPropertyIL(propertyInfo, generator); 

      return (Func<T, object>)dynamicMethod.CreateDelegate(typeof(Func<T, object>)); 
     } 

     private static DynamicMethod CreateDynamicMethod(string name, Type returnType, Type[] parameterTypes, Type owner) 
     { 
      DynamicMethod dynamicMethod = new DynamicMethod(name, returnType, parameterTypes, owner, true); 

      return dynamicMethod; 
     } 

     private static void GenerateCreateGetPropertyIL(PropertyInfo propertyInfo, ILGenerator generator) 
     { 
      MethodInfo getMethod = propertyInfo.GetGetMethod(true); 
      if (getMethod == null) 
      { 
       throw new ArgumentException("Property " + propertyInfo.Name + " does not have a getter."); 
      } 

      if (!getMethod.IsStatic) 
      { 
       generator.PushInstance(propertyInfo.DeclaringType); 
      } 

      generator.CallMethod(getMethod); 
      generator.BoxIfNeeded(propertyInfo.PropertyType); 
      generator.Return(); 
     } 
    } 

    internal static class ILGeneratorExtensions 
    { 
     public static void PushInstance(this ILGenerator generator, Type type) 
     { 
      generator.Emit(OpCodes.Ldarg_0); 
      if (type.IsValueType) 
      { 
       generator.Emit(OpCodes.Unbox, type); 
      } 
      else 
      { 
       generator.Emit(OpCodes.Castclass, type); 
      } 
     } 

     public static void PushArrayInstance(this ILGenerator generator, int argsIndex, int arrayIndex) 
     { 
      generator.Emit(OpCodes.Ldarg, argsIndex); 
      generator.Emit(OpCodes.Ldc_I4, arrayIndex); 
      generator.Emit(OpCodes.Ldelem_Ref); 
     } 

     public static void BoxIfNeeded(this ILGenerator generator, Type type) 
     { 
      if (type.IsValueType) 
      { 
       generator.Emit(OpCodes.Box, type); 
      } 
      else 
      { 
       generator.Emit(OpCodes.Castclass, type); 
      } 
     } 

     public static void UnboxIfNeeded(this ILGenerator generator, Type type) 
     { 
      if (type.IsValueType) 
      { 
       generator.Emit(OpCodes.Unbox_Any, type); 
      } 
      else 
      { 
       generator.Emit(OpCodes.Castclass, type); 
      } 
     } 

     public static void CallMethod(this ILGenerator generator, MethodInfo methodInfo) 
     { 
      if (methodInfo.IsFinal || !methodInfo.IsVirtual) 
      { 
       generator.Emit(OpCodes.Call, methodInfo); 
      } 
      else 
      { 
       generator.Emit(OpCodes.Callvirt, methodInfo); 
      } 
     } 

     public static void Return(this ILGenerator generator) 
     { 
      generator.Emit(OpCodes.Ret); 
     } 
    } 
} 
+2

:あなたが問題を再現する小さなアプリを見ることができます下

。問題のリンクはこちら:https://github.com/JamesNK/Newtonsoft.Json/issues – dbc

+2

クイック返信ありがとう!うん、私はそれが今サポートされていない恐れ、私はここで問題を作成しました:https://github.com/JamesNK/Newtonsoft.Json/issues/1552 – Sergio0694

関連する問題