Получите динамический объект для JsonConvert.DeserializeObject, делая свойства прописными

Я не знаю, как обратиться за помощью, и это немного необычно, я согласен, поэтому, пожалуйста, извините меня. Я попытаюсь объяснить, как показано ниже:

• Я использую JSON с использованием POST и получаю динамический объект. Мне нужно преобразовать все входящие свойства в динамический объект в верхний регистр.

• Я использую то, что мне кажется неправильным решением. Я конвертирую JSON в строковый словарь, затем помещаю значения в новый словарь после преобразования ключа в Key.ToUpper(), а затем десериализую его обратно в динамический объект.

Текущее рабочее решение выглядит следующим образом:

string _StrJSON = @"{""FIELDA"" : ""1234"",""fieldb"" : ""OtherValue""}";

var d = JsonConvert.DeserializeObject<Dictionary<string, string>>(_StrJSON);

d.ContainsKey("FIELDB").Dump(); // returns false, obviously.

Dictionary<string, string> dr = new Dictionary<string, string>();

d.ToList().ForEach(r => dr.Add(r.Key.ToUpper(), r.Value));

dr.Dump("dr"); // FIELDA 1234  - FIELDB OtherValue 

var a  = JsonConvert.SerializeObject(dr);
a.Dump(); // {"FIELDA":"1234","FIELDB":"OtherValue"}

Это работает.

[EDIT] Некоторая путаница с моим "var" выше и другими вещами. Я очень четко понимаю, что динамично, а что нет. [/РЕДАКТИРОВАТЬ]

То, что я пробовал до сих пор, НЕ РАБОТАЕТ, как показано ниже:

namespace Newtonsoft.Json
{
/// <summary>
/// Converts JSON keys to uppercase.
/// </summary>
public class UppercaseContractResolver : Serialization.DefaultContractResolver
{
    #region Methods.

    #region Public Methods.

    /// <summary>
    /// Resolves property name for this JSON object.
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    protected override string ResolvePropertyName(string key)
    {
        return key.ToUpper();
    }

    #endregion

    #endregion

    #region Constructors.

    /// <summary>
    /// Converts JSON keys to uppercase.
    /// </summary>
    public UppercaseContractResolver()
    {
    }

    #endregion
}

/// <summary>
/// Wrapper for Newtonsoft.Json.JsonConvert for JSON keys as uppercase.
/// </summary>
public static class JsonConvertUpper
{
    #region Members.        
    #endregion

    #region Methods.

    #region Public Methods.

    /// <summary>
    /// Tries to serialize specified object with JSON keys in upper case.
    /// <para>e.g. "key":value should become "KEY":value by using this method.</para>
    /// </summary>
    /// <param name="value">Object.</param>
    /// <returns></returns>
    public static string SerializeObjectUpper(object value)
    {
        return JsonConvert.SerializeObject(value, new JsonSerializerSettings
        {
            ContractResolver = new UppercaseContractResolver()
        });
    }

    /// <summary>
    /// Tries to Deserialize specified object with JSON keys in upper case.
    /// <para>e.g. "key":value should become "KEY":value by using this method.</para>
    /// </summary>
    /// <param name="strValue">String.</param>
    /// <returns></returns>
    public static object DeserializeObjectUpper(string strValue)
    {
        // DeserializeObject does not allow uppercase properties. So use SerializeObjectUpper and then deserialize.

        var value = JsonConvert.DeserializeObject(strValue);

        string strJSON = SerializeObjectUpper(value);

        return JsonConvert.DeserializeObject(strJSON, new JsonSerializerSettings()
        {
            ContractResolver = new UppercaseContractResolver()
        });
    }

    #endregion

    #endregion
}
}

Вызов метода для вышеуказанного будет:

  dynamic json = JsonConvertUpper.DeserializeObjectUpper(_StrJSON);

                if (json.CTN== null)
                {...}

[EDIT] Обратите внимание, что у меня нет классов, поскольку ключи JSON должны рассматриваться как ПРОПИСНЫЕ, а классы и другой код и т. д. - все в правильном регистре. [/РЕДАКТИРОВАТЬ]

Есть ли способ сделать это внутри JsonConvert? Чтобы избежать моего ручного патча? Любая помощь приветствуется. Спасибо.


person Fawad Raza    schedule 03.03.2016    source источник
comment
Вы можете переименовать свойства, используя атрибут JsonProperty в вашем классе (ах).   -  person Ňɏssa Pøngjǣrdenlarp    schedule 03.03.2016
comment
Обратите внимание, что динамические объекты определяются во время выполнения. Это не динамические типы. переменные var являются строго типизированными переменными. Просто хотел очистить эту терминологию здесь   -  person misha130    schedule 03.03.2016
comment
Да, я знаю, но я отредактировал свой вопрос.   -  person Fawad Raza    schedule 03.03.2016


Ответы (1)


Причина, по которой ваш сопоставитель контрактов не работает, заключается в том, что сопоставитель контрактов определяет, как отображать JSON из и в свойства POCO при сериализации, но когда вы десериализуете в dynamic, Json.NET на самом деле не отображает из и в POCO, он создает JToken непосредственно из JSON, элементы которого реализовать IDynamicMetaObjectProvider. т.е.

dynamic o = JsonConvert.DeserializeObject(strJSON);

а также

dynamic o = JToken.Parse(strJSON);

подобные.

Учитывая, что это так, самый простой способ сделать то, что вы хотите, — это явно преобразовать имя каждого свойства в верхний регистр по мере построения иерархии, например:

public static class JsonExtensions
{
    public static JToken ParseUppercase(string json)
    {
        using (var textReader = new StringReader(json))
        using (var jsonReader = new JsonTextReader(textReader))
        {
            return jsonReader.ParseUppercase();
        }
    }

    public static JToken ParseUppercase(this JsonReader reader)
    {
        return reader.Parse(n => n.ToUpperInvariant());
    }

    public static JToken Parse(this JsonReader reader, Func<string, string> nameMap)
    {
        JToken token;
        using (var writer = new RenamingJTokenWriter(nameMap))
        {
            writer.WriteToken(reader);
            token = writer.Token;
        }

        return token;
    }
}

class RenamingJTokenWriter : JTokenWriter
{
    readonly Func<string, string> nameMap;

    public RenamingJTokenWriter(Func<string, string> nameMap)
        : base()
    {
        if (nameMap == null)
            throw new ArgumentNullException();
        this.nameMap = nameMap;
    }

    public override void WritePropertyName(string name)
    {
        base.WritePropertyName(nameMap(name));
    }

    // No need to override WritePropertyName(string name, bool escape) since it calls WritePropertyName(string name)
}

А затем используйте его как:

dynamic json = JsonExtensions.ParseUppercase(_StrJSON);

Пример скрипта.

person dbc    schedule 03.03.2016
comment
Ах, @dbc, абсолютно красиво! Большое спасибо. :) - person Fawad Raza; 04.03.2016
comment
Сегодня утром я пытался использовать JToken и JArray со всеми видами циклов для массивов внутри объектов. Я протестировал скрипку с помощью string _StrJSON = @"{""FIELDA"" : ""1234"",""fieldb"" : ""OtherValue"", ""ARRAYDATA"": [ {""FIELD1"":""ArrayPropOne"", ""ArrayInsideArray1"": [ {""Fieldx"":""InsideArray1"", ""ArrayInsideArray2"": [ {""Fieldxy"":""InsideArray2""} ] } ]} ] }";. Она отлично работает. Большое спасибо за это, высоко ценится. - person Fawad Raza; 04.03.2016