Es klingt wie Sie versuchen, Json.NET TypeNameHandling
setting neu zu erfinden. Da Sie diese Einstellung nicht verwenden, sondern stattdessen den Typnamen für CurrentValue
selbst serialisieren, müssen Sie custom JsonConverter
erstellen, um Ihre PropertyValue
und Deserialize CurrentValue
mit dem gewünschten Typ zu füllen. Ohne diesen wird JSER.NET den aktuellen Wert entweder für ein primitives Objekt (wie long
oder string
) oder ein LINQ to JSON Objekt wie JObject
für einen nicht primitiven JSON-Wert deserialisieren. (Letzteres ist der String-Wert mit geschweiften Klammern eingewickelt um die zwei oder drei Schlüssel/Wert-Paare, die Sie in den Kommentaren zu sehen erwähnen.)
ist hier ein möglicher Konverter auf Ihren Typ angewendet:
[JsonConverter(typeof(PropertyValueConverter))]
public sealed class PropertyValue
{
public PropertyValue(object CurrentValue)
{
SetCurrentValue(CurrentValue);
}
public PropertyValue()
{
}
public string PropertyName { get; set; }
public string CategoryName { get; set; }
public string DisplayName { get; set; }
public int PropertyId { get; set; }
public string TypeName { get; set; }
public string ToolTip { get; set; }
public string Description { get; set; }
public object CurrentValue { get; set; }
public void SetCurrentValue(object value)
{
CurrentValue = value;
if (value == null)
TypeName = null;
else
TypeName = value.GetType().AssemblyQualifiedName;
}
}
public class PropertyValueConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(PropertyValue).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var propertyValue = (existingValue as PropertyValue ?? new PropertyValue());
var obj = JObject.Load(reader);
// Remove the CurrentValue property for manual deserialization, and deserialize
var jValue = obj.GetValue("CurrentValue", StringComparison.OrdinalIgnoreCase).RemoveFromLowestPossibleParent();
// Load the remainder of the properties
serializer.Populate(obj.CreateReader(), propertyValue);
// Convert the type name to a type.
// Use the serialization binder to sanitize the input type! See
// https://stackoverflow.com/questions/39565954/typenamehandling-caution-in-newtonsoft-json
if (!string.IsNullOrEmpty(propertyValue.TypeName) && jValue != null)
{
string typeName, assemblyName;
ReflectionUtils.SplitFullyQualifiedTypeName(propertyValue.TypeName, out typeName, out assemblyName);
var type = serializer.Binder.BindToType(assemblyName, typeName);
if (type != null)
propertyValue.SetCurrentValue(jValue.ToObject(type, serializer));
}
return propertyValue;
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
public static class JsonExtensions
{
public static JToken RemoveFromLowestPossibleParent(this JToken node)
{
if (node == null)
return null;
var contained = node.AncestorsAndSelf().Where(t => t.Parent is JContainer && t.Parent.Type != JTokenType.Property).FirstOrDefault();
if (contained != null)
contained.Remove();
// Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should
if (node.Parent is JProperty)
((JProperty)node.Parent).Value = null;
return node;
}
}
public static class ReflectionUtils
{
// Utilities taken from https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs
// I couldn't find a way to access these directly.
public static void SplitFullyQualifiedTypeName(string fullyQualifiedTypeName, out string typeName, out string assemblyName)
{
int? assemblyDelimiterIndex = GetAssemblyDelimiterIndex(fullyQualifiedTypeName);
if (assemblyDelimiterIndex != null)
{
typeName = fullyQualifiedTypeName.Substring(0, assemblyDelimiterIndex.GetValueOrDefault()).Trim();
assemblyName = fullyQualifiedTypeName.Substring(assemblyDelimiterIndex.GetValueOrDefault() + 1, fullyQualifiedTypeName.Length - assemblyDelimiterIndex.GetValueOrDefault() - 1).Trim();
}
else
{
typeName = fullyQualifiedTypeName;
assemblyName = null;
}
}
private static int? GetAssemblyDelimiterIndex(string fullyQualifiedTypeName)
{
int scope = 0;
for (int i = 0; i < fullyQualifiedTypeName.Length; i++)
{
char current = fullyQualifiedTypeName[i];
switch (current)
{
case '[':
scope++;
break;
case ']':
scope--;
break;
case ',':
if (scope == 0)
{
return i;
}
break;
}
}
return null;
}
}
Probe fiddle. (Ich musste mehrere Ihrer Eigenschaften machen lesen/schreiben, da sie schreibgeschützt wurden, aber nicht im Konstruktor festgelegt.)
Alternativ können Sie markieren Sie Ihre CurrentValue
mit [JsonProperty(TypeNameHandling = TypeNameHandling.All)]
:
public sealed class PropertyValue
{
[JsonProperty(TypeNameHandling = TypeNameHandling.All)]
public object CurrentValue { get; set; }
// Remainder as before
Nachdem dies getan eine "$type"
Eigenschaft Ausgabe die tatsächliche Art der CurrentObject
angibt, und automatisch diese Art bei der Deserialisierung verwenden, zB Json.NET wird:
{
"CurrentValue": {
"$type": "Question42537050.ExampleClass1, Tile",
"Foo": "hello"
},
"PropertyName": "name1",
"CategoryName": null,
"DisplayName": null,
"PropertyId": 0,
"TypeName": "Question42537050.ExampleClass1, Tile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"ToolTip": "tip1",
"Description": null
}
natürlich, wenn Sie dies tun, die t Ihr Name erscheint zweimal im JSON - einmal für Ihre TypeName
Eigenschaft und einmal für Json.NETs $type
Eigenschaft. Und diese Einstellung funktioniert nur für komplexe Objekte und Arrays, nicht für primitive Typen.
In beiden Fällen sollten Sie aus Sicherheitsgründen Ihre vor dem Erstellen einer Instanz des Typs aus Gründen, die here erläutert werden, bereinigen. Mein Code geht davon aus, dass Sie JsonSerializer.Binder
eingerichtet haben, dies mit einem custom SerializationBinder
zu tun, aber Sie können stattdessen einige Validierungslogik in PropertyValueConverter.ReadJson()
selbst implementieren.
ist der CurrentValue Null im Debugger oder wirft es Sie einen Fehler? – Coder
Es zeigt tatsächlich einen Wert in der JSON-Zeichenfolge, die gelesen wird, aber nach Deserialisierung, zeigt es nur als Zeichenfolge Wert mit geschweiften Klammern um die zwei oder drei Schlüssel/Wert-Paare gewickelt –
können Sie Ihre JSON teilen? – Coder