JObject.SelectToken Equivalent in .NET

前端 未结 1 1968
无人共我
无人共我 2021-01-23 19:26

I need to remove the outer node of a JSON. So an example would be :

{
    app: {
       ...
    }
}

Any ideas on how to remove the outer node,

相关标签:
1条回答
  • 2021-01-23 19:40

    There is no equivalent to SelectToken built into .Net. But if you simply want to unwrap an outer root node and do not know the node name in advance, you have the following options.

    1. If you are using .Net 4.5 or later, you can deserialize to a Dictionary<string, T> with DataContractJsonSerializer.UseSimpleDictionaryFormat = true:

      protected T DeserializeNestedJsonString<T>(string jsonString)
      {
          using (var memoryStream = new MemoryStream(Encoding.Unicode.GetBytes(jsonString)))
          {
              var serializer = new DataContractJsonSerializer(typeof(Dictionary<string, T>));
              serializer.UseSimpleDictionaryFormat = true;
              var dictionary = (Dictionary<string, T>)serializer.ReadObject(memoryStream);
              if (dictionary == null || dictionary.Count == 0)
                  return default(T);
              else if (dictionary.Count == 1)
                  return dictionary.Values.Single();
              else
              {
                  throw new InvalidOperationException("Root object has too many properties");
              }
          }
      }
      

      Note that if your root object contains more than one property, you cannot deserialize to a Dictionary<TKey, TValue> to get the first property since the order of the items in this class is undefined.

    2. On any version of .Net that supports the data contract serializers, you can take advantage of the fact that DataContractJsonSerializer inherits from XmlObjectSerializer to call JsonReaderWriterFactory.CreateJsonReader() to create an XmlReader that actually reads JSON, then skip forward to the first nested "element":

      protected T DeserializeNestedJsonStringWithReader<T>(string jsonString)
      {
          var reader = JsonReaderWriterFactory.CreateJsonReader(Encoding.Unicode.GetBytes(jsonString), System.Xml.XmlDictionaryReaderQuotas.Max);
          int elementCount = 0;
      
          while (reader.Read())
          {
              if (reader.NodeType == System.Xml.XmlNodeType.Element)
                  elementCount++;
              if (elementCount == 2) // At elementCount == 1 there is a synthetic "root" element
              {
                  var serializer = new DataContractJsonSerializer(typeof(T));
                  return (T)serializer.ReadObject(reader, false);
              }
          }
          return default(T);
      }
      

      This technique looks odd (parsing JSON with an XmlReader?), but with some extra work it should be possible to extend this idea to create SAX-like parsing functionality for JSON that is similar to SelectToken(), skipping forward in the JSON until a desired property is found, then deserializing its value.

      For instance, to select and deserialize specific named properties, rather than just the first root property, the following can be used:

      public static class DataContractJsonSerializerExtensions
      {
          public static T DeserializeNestedJsonProperty<T>(string jsonString, string rootPropertyName)
          {
              // Check for count == 2 because there is a synthetic <root> element at the top.
              Predicate<Stack<string>> match = s => s.Count == 2 && s.Peek() == rootPropertyName;
              return DeserializeNestedJsonProperties<T>(jsonString, match).FirstOrDefault();
          }
      
          public static IEnumerable<T> DeserializeNestedJsonProperties<T>(string jsonString, Predicate<Stack<string>> match)
          {
              DataContractJsonSerializer serializer = null;
              using (var reader = JsonReaderWriterFactory.CreateJsonReader(Encoding.UTF8.GetBytes(jsonString), XmlDictionaryReaderQuotas.Max))
              {
                  var stack = new Stack<string>();
                  while (reader.Read())
                  {
                      if (reader.NodeType == System.Xml.XmlNodeType.Element)
                      {
                          stack.Push(reader.Name);
                          if (match(stack))
                          {
                              serializer = serializer ?? new DataContractJsonSerializer(typeof(T));
                              yield return (T)serializer.ReadObject(reader, false);
                          }
                          if (reader.IsEmptyElement)
                              stack.Pop();
                      }
                      else if (reader.NodeType == XmlNodeType.EndElement)
                      {
                          stack.Pop();
                      }
                  }
              }
          }
      }
      

      See Mapping Between JSON and XML for details on how JsonReaderWriterFactory maps JSON to XML.

    0 讨论(0)
提交回复
热议问题