【问题标题】:Json.NET Deserialization into dynamic object with referencingJson.NET 反序列化为具有引用的动态对象
【发布时间】:2015-07-14 19:13:11
【问题描述】:

如何让 Json.NET 反序列化为动态对象但仍进行引用解析?
dynamic d=JsonConvert.DeserializeObject<ExpandoObject>(...) 就像
dynamic d=JsonConvert.DeserializeObject(...) 返回一个动态对象但它们不解析 $ref$id 零件。 (例如 ExpandoObject eo 将只有 eo["$ref"]="..." 并且没有它应该具有的属性,因为它与 $id-Object 不同)

我发现我需要合约解析器解析为动态合约 - ExpandoObject 仅在我使用自定义 ContractResolver 明确告诉 Json.NET 时才会这样做。

似乎ExpandoObject 是用它自己的转换器解析的,但它又失败了。

我尝试了一个从 IDynamicMetaObjectProvider 继承的自定义类,这导致了无限循环,并且看起来不正确。我实际上希望有一些简单的解决方案可以让 ExpandoObject 获得参考分辨率。

有什么帮助吗?

【问题讨论】:

    标签: c# dynamic json.net


    【解决方案1】:

    我现在的做法是使用一个后处理步骤和一个递归函数来进行自己的引用保存和重新布线:

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

      【讨论】:

      • 哦,我错过了一个事实,即转换器实际上将串行器作为使用 ReferenceResolver 的参数。我对类似的解决方案犹豫不决,因为我认为这将是大量的复制粘贴......
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-30
      • 2015-04-20
      • 1970-01-01
      • 2017-06-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多