Эквивалент JObject.SelectToken в.NET

Мне нужно удалить внешний узел JSON. Вот пример:

{
    app: {
       ...
    }
}

Любые идеи о том, как удалить внешний узел, поэтому мы получаем только

{
   ...
}

БЕЗ использования JSON.NET, только инструменты в.NET Framework (C#).

В Json.NET я использовал:

JObject.Parse(json).SelectToken("app").ToString();

В качестве альтернативы, любая конфигурация DataContractJsonSerializer, так что он игнорирует корень при десериализации, также будет работать. Теперь я делаю десериализацию так:

protected T DeserializeJsonString<T>(string jsonString)
        {
            T tempObject = default(T);

            using (var memoryStream = new MemoryStream(Encoding.Unicode.GetBytes(jsonString)))
            {
                var serializer = new DataContractJsonSerializer(typeof(T));
                tempObject = (T)serializer.ReadObject(memoryStream);
            }

            return tempObject;
        }

Обратите внимание, что имя свойства корневого объекта может отличаться от случая к случаю. Например, это может быть "transaction",

Спасибо за любое предложение.

1 ответ

Решение

Там нет эквивалента SelectToken встроенный в.Net. Но если вы просто хотите развернуть внешний корневой узел и заранее не знаете имя узла, у вас есть следующие варианты.

  1. Если вы используете.Net 4.5 или более позднюю версию, вы можете Dictionary<string, T> с 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");
            }
        }
    }
    

    Обратите внимание, что если ваш корневой объект содержит более одного свойства, вы не можете десериализовать в Dictionary<TKey, TValue> получить первое свойство, поскольку порядок элементов в этом классе не определен.

  2. В любой версии.Net, поддерживающей сериализаторы контрактов данных, вы можете воспользоваться тем, что DataContractJsonSerializer наследуется от XmlObjectSerializer звонить JsonReaderWriterFactory.CreateJsonReader() создать XmlReader который на самом деле читает JSON, а затем переходит к первому вложенному "элементу":

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

    Эта техника выглядит странно (разбор JSON с XmlReader?), но с некоторой дополнительной работой должно быть возможно расширить эту идею, чтобы создать SAX-подобную функциональность синтаксического анализа для JSON, которая похожа на SelectToken(), пропуская вперед в JSON, пока не будет найдено требуемое свойство, затем десериализовать его значение.

    Например, для выбора и десериализации определенных именованных свойств, а не только первого корневого свойства, может использоваться следующее:

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

    Посмотрите Отображение Между JSON и XML для деталей о том, как JsonReaderWriterFactory отображает JSON в XML.

Другие вопросы по тегам