2015-05-05 26 views
4

我怎样才能让Json.NET反序列化为动态对象,但仍然做参考解决?
dynamic d=JsonConvert.DeserializeObject<ExpandoObject>(...)就像
dynamic d=JsonConvert.DeserializeObject(...)返回一个动态对象,但它们不解决$ref$id部分。 (例如一个ExpandoObject eo只会有eo["$ref"]="...",没有它应有的性能,因为它是不一样的$id -object)Json.NET反序列化到动态对象引用

我已经找到了,是我所需要的合同解析器解析成动态合约 - 如果我使用自定义ContractResolver明确告诉Json.NET,则只有ExpandoObject

仍然看起来ExpandoObject是用它自己的Converter解析的,它又失败了。

我试过一个自定义的类继承IDynamicMetaObjectProvider,导致无限循环,似乎不是正确的事情。我实际上会期待一些简单的解决方案,以获得ExpandoObject参考分辨率。

任何帮助?

回答

0

我现在做的方式是后处理步骤和递归功能,它们正在执行自己的参考保存和重新布线:

private static void Reffing(this IDictionary<string, object> current, Action<object> exchange,IDictionary<string, object> refdic) 
    { 
     object value; 
     if(current.TryGetValue("$ref", out value)) 
     { 
      if(!refdic.TryGetValue((string) value, out value)) 
       throw new Exception("ref not found "); 
      if (exchange != null) 
       exchange(value); 
      return; 
     } 
     if (current.TryGetValue("$id", out value)) 
     { 
      refdic[(string) value] = current; 
     } 
     foreach (var kvp in current.ToList()) 
     { 
      if (kvp.Key.StartsWith("$")) 
       continue; 
      var expandoObject = kvp.Value as ExpandoObject; 
      if(expandoObject != null) 
       Reffing(expandoObject,o => current[kvp.Key]=o,refdic); 
      var list = kvp.Value as IList<object>; 
      if (list == null) continue; 
      for (var i = 0; i < list.Count; i++) 
      { 
       var lEO = list[i] as ExpandoObject; 
       if(lEO!=null) 
        Reffing(lEO,o => list[i]=o,refdic); 
      } 
     } 
    } 

用作:

 var test = JsonConvert.DeserializeObject<ExpandoObject>(...); 
     var dictionary = new Dictionary<string, object>(); 
     Reffing(test,null,dictionary); 
2

由于Json.NET是开源的,它的MIT许可allows modification,最简单的解决方案可能是其ExpandoObjectConverter适应您的需求:

/// <summary> 
/// Converts an ExpandoObject to and from JSON, handling object references. 
/// </summary> 
public class ObjectReferenceExpandoObjectConverter : JsonConverter 
{ 
    // Adapted from https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Converters/ExpandoObjectConverter.cs 
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     // can write is set to false 
     throw new NotImplementedException(); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     return ReadValue(serializer, reader); 
    } 

    private object ReadValue(JsonSerializer serializer, JsonReader reader) 
    { 
     while (reader.TokenType == JsonToken.Comment) 
     { 
      if (!reader.Read()) 
       throw reader.CreateException("Unexpected end when reading ExpandoObject."); 
     } 

     switch (reader.TokenType) 
     { 
      case JsonToken.StartObject: 
       return ReadObject(serializer, reader); 
      case JsonToken.StartArray: 
       return ReadList(serializer, reader); 
      default: 
       if (JsonTokenUtils.IsPrimitiveToken(reader.TokenType)) 
        return reader.Value; 
       throw reader.CreateException("Unexpected token when converting ExpandoObject"); 
     } 
    } 

    private object ReadList(JsonSerializer serializer, JsonReader reader) 
    { 
     IList<object> list = new List<object>(); 

     while (reader.Read()) 
     { 
      switch (reader.TokenType) 
      { 
       case JsonToken.Comment: 
        break; 
       default: 
        object v = ReadValue(serializer, reader); 
        list.Add(v); 
        break; 
       case JsonToken.EndArray: 
        return list; 
      } 
     } 

     throw reader.CreateException("Unexpected end when reading ExpandoObject."); 
    } 

    private object ReadObject(JsonSerializer serializer, JsonReader reader) 
    { 
     IDictionary<string, object> expandoObject = null; 
     object referenceObject = null; 

     while (reader.Read()) 
     { 
      switch (reader.TokenType) 
      { 
       case JsonToken.PropertyName: 
        string propertyName = reader.Value.ToString(); 
        if (!reader.Read()) 
         throw new InvalidOperationException("Unexpected end when reading ExpandoObject."); 
        object v = ReadValue(serializer, reader); 
        if (propertyName == "$ref") 
        { 
         var id = (v == null ? null : Convert.ToString(v, CultureInfo.InvariantCulture)); 
         referenceObject = serializer.ReferenceResolver.ResolveReference(serializer, id); 
        } 
        else if (propertyName == "$id") 
        { 
         var id = (v == null ? null : Convert.ToString(v, CultureInfo.InvariantCulture)); 
         serializer.ReferenceResolver.AddReference(serializer, id, (expandoObject ?? (expandoObject = new ExpandoObject()))); 
        } 
        else 
        { 
         (expandoObject ?? (expandoObject = new ExpandoObject()))[propertyName] = v; 
        } 
        break; 
       case JsonToken.Comment: 
        break; 
       case JsonToken.EndObject: 
        if (referenceObject != null && expandoObject != null) 
         throw reader.CreateException("ExpandoObject contained both $ref and real data"); 
        return referenceObject ?? expandoObject; 
      } 
     } 

     throw reader.CreateException("Unexpected end when reading ExpandoObject."); 
    } 

    public override bool CanConvert(Type objectType) 
    { 
     return (objectType == typeof(ExpandoObject)); 
    } 

    public override bool CanWrite 
    { 
     get { return false; } 
    } 
} 

public static class JsonTokenUtils 
{ 
    // Adapted from https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Utilities/JsonTokenUtils.cs 
    public static bool IsPrimitiveToken(this JsonToken token) 
    { 
     switch (token) 
     { 
      case JsonToken.Integer: 
      case JsonToken.Float: 
      case JsonToken.String: 
      case JsonToken.Boolean: 
      case JsonToken.Undefined: 
      case JsonToken.Null: 
      case JsonToken.Date: 
      case JsonToken.Bytes: 
       return true; 
      default: 
       return false; 
     } 
    } 
} 

public static class JsonReaderExtensions 
{ 
    public static JsonSerializationException CreateException(this JsonReader reader, string format, params object[] args) 
    { 
     // Adapted from https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/JsonPosition.cs 

     var lineInfo = reader as IJsonLineInfo; 
     var path = (reader == null ? null : reader.Path); 
     var message = string.Format(CultureInfo.InvariantCulture, format, args); 
     if (!message.EndsWith(Environment.NewLine, StringComparison.Ordinal)) 
     { 
      message = message.Trim(); 
      if (!message.EndsWith(".", StringComparison.Ordinal)) 
       message += "."; 
      message += " "; 
     } 
     message += string.Format(CultureInfo.InvariantCulture, "Path '{0}'", path); 
     if (lineInfo != null && lineInfo.HasLineInfo()) 
      message += string.Format(CultureInfo.InvariantCulture, ", line {0}, position {1}", lineInfo.LineNumber, lineInfo.LinePosition); 
     message += "."; 

     return new JsonSerializationException(message); 
    } 
} 

,然后用它喜欢:

 var settings = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects, ReferenceLoopHandling = ReferenceLoopHandling.Serialize }; 
     settings.Converters.Add(new ObjectReferenceExpandoObjectConverter()); 
     dynamic d = JsonConvert.DeserializeObject<ExpandoObject>(json, settings); 
+0

哦,我错过了一个事实,即转换器实际获得的序列化作为使用ReferenceResolver的参数。我对于类似的解决方案犹豫不决,因为我认为这将会如此复制粘贴... –