2017-12-23 1 views
3

Angenommen, ich eine Klasse wie diese:Wie wird ein C# ref-Feld mit Newtonsoft.Json serialisiert?

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

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

Hinweis: Diese Klasse ist ein abgespeckte Beispiel in meinem realen Welt Szenario, dass Feld nicht Mitglied Feld ist, sonst I Ich habe gerade das Json-Attribut hinzugefügt. Dieses Feld ist ein Feld, das von einem anderen Objekt, das ein Mitglied der Klasse ist, freigelegt wird. Die öffentliche Eigenschaft gibt dieses Feld nur für den Benutzer frei, um den Zugriff auf diesen Wert zu erleichtern.

Auch, die tatsächliche Eigenschaft tipe ist nichtint, sondern ein 12-Byte struct, so dass ich zurückkehren es durch Bezugnahme, um die nutzlose Kopie von Wert zu vermeiden.

Ich serialisieren eine solche Klasse mit JsonConvert.SerializeObject(this, Formatting.Indented).

Die Newtonsoft.Json löst eine Ausnahme aus, wenn der Wert Umwandlung, sagen, es nicht das Feld/Eigenschaftswert zugreifen kann (ich denke, es ist ein ref Parameter ist das Reflexionsverfahren macht von der Bibliothek Absturz verwendet).

Ich habe versucht, mit einem benutzerdefinierten JsonConverter experimentieren, aber der Absturz passiert, bevor ein zusätzlicher Konverter überhaupt verwendet wird.

Ich weiß, eine schnelle Lösung wäre, einen sekundären, privaten Parameter hinzuzufügen, der nur dieses Feld als Wert und nicht durch Verweis zurückgibt, und nur für die Json-Serialisierung verwendet, aber sieht nur schlecht für mich aus (und ich würde muss die automatische VS-Warnung über nicht verwendete private Parameter auch deaktivieren), und ich suche nach einer besseren Lösung, wenn möglich (ohne nutzlose Felder/Eigenschaften einzuführen).

Danke für Ihre Hilfe!

Antwort

2

Dies ist zu lang, um ein Kommentar zu sein, wenn jemand eine andere Antwort postet, werde ich es löschen. Mit einem schnellen Blick können Sie es im Moment nicht überschreiben.

Das Problem tritt in DynamicValueProvider.cs Linie 110:

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); 
     } 
} 

Die Ursache ist in CreateGet es kein Verfahren, um diese Arten korrekt zu verarbeiten generieren. Vielleicht solltest du ein neues Thema auf GitHub öffnen (falls es noch keins gibt).

Im folgenden finden Sie eine kleine Anwendung sehen, der das Problem reproduziert:

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

Ich konnte keine vorbestehenden Problem finden, so ich diese Berichterstattung empfehlen würde. Link für Probleme ist hier: https://github.com/JamesNK/Newtonsoft.Json/issues – dbc

+2

Danke für die schnelle Antwort! Ja, ich befürchtete, dass es im Moment nicht unterstützt wird. Ich habe hier ein Problem erstellt: https://github.com/JamesNK/Newtonsoft.Json/issues/1552 – Sergio0694

Verwandte Themen