2015-05-05 4 views
4

Как я могу заставить Json.NET десериализоваться на динамические объекты, но все же сделать ссылочное разрешение?
dynamic d=JsonConvert.DeserializeObject<ExpandoObject>(...) как
dynamic d=JsonConvert.DeserializeObject(...) возвращает динамический объект, но они не разрешают $ref и $id частей. (An ExpandoObject eo, например, будет иметь eo["$ref"]="..." только и не имеет свойства она должна иметь, потому что это не то же самое, как $id -объект)Json.NET Deserialization в динамическом объекте с привязкой

То, что я узнал, что мне нужно контракт распознаватель разрешаться в динамический контракт - который ExpandoObject только делает, если я прямо говорю Json.NET с пользовательским ContractResolver.

По-прежнему кажется, что ExpandoObject разобрался с собственным конвертером, и он снова не работает.

Я пробовал собственный класс, наследующий от 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

ой, я пропустил тот факт, что конвертер фактически получает Serializer в качестве параметра для использования ReferenceResolver. Я не решался на подобное решение, поскольку я думал, что это будет так много копировать ... –

Смежные вопросы