Fawad Raza
Fawad Raza

Reputation: 91

Get a dynamic object for JsonConvert.DeserializeObject making properties uppercase

I am not sure how to seek help and this is kind of unusual I agree, so please pardon me. I'll try to explain as below:

• I am consuming JSON using POST and getting a dynamic object. I need to convert all incoming properties in dynamic object to uppercase.

• I am using what I think is a bodged solution. I'm converting JSON to string Dictionary, then putting values into a new dictionary after converting the Key to Key.ToUpper() and then deserializing it back into a dynamic object.

Current working solution is as follows:

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"}

This works.

[EDIT] Some confusion about my "var" above and other stuff. I am very clear on what is dynamic and what isn't. [/EDIT]

What I have tried so far which DOES NOT WORK is as below:

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

The method call for above would be:

  dynamic json = JsonConvertUpper.DeserializeObjectUpper(_StrJSON);

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

[EDIT] Please note I don't have classes as JSON keys have to be treated as UPPERCASE and classes and other code etc are all in ProperCase. [/EDIT]

Any way of doing it inside JsonConvert at all? To avoid my manual patch? Any help is appreciated. Thanks.

Upvotes: 1

Views: 4742

Answers (1)

dbc
dbc

Reputation: 116981

The reason that your contract resolver does not work is that Json.NET's contract resolver defines how to map JSON from and to the properties of a POCO when serializing -- but when you deserialize to dynamic, Json.NET isn't actually mapping from and to a POCO, it's building up a JToken hierarchy directly from the JSON, the members of which implement IDynamicMetaObjectProvider. I.e.

dynamic o = JsonConvert.DeserializeObject(strJSON);

and

dynamic o = JToken.Parse(strJSON);

are the same.

Given that this is the case, the easiest way to do what you want would be to explicitly convert each property name to uppercase as the hierarchy is being built, like so:

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

And then use it like:

dynamic json = JsonExtensions.ParseUppercase(_StrJSON);

Sample fiddle.

Upvotes: 4

Related Questions