Romonov Romonov - 3 months ago 112
JSON Question

JsonConvert DeserializeObject case sensitive

I am trying to deserialize a string content into an object, but I want the content to be case sensitive. The code should only succeed if the string has lower case properties and fail if it has upper case properties. Following is the class:

internal class ResponseList
{
[DataMember]
[JsonProperty]
internal List<Response> Value { get; set; }
}

internal class Response
{
[DataMember]
[JsonProperty]
internal string Id { get; set; }

[DataMember]
[JsonProperty]
internal string Location { get; set; }

[DataMember]
[JsonProperty]
internal PlanClass Plan { get; set; }
}

internal class PlanClass
{
[DataMember]
[JsonProperty]
internal string Name { get; set; }

[DataMember]
[JsonProperty]
internal string Product { get; set; }

[DataMember]
[JsonProperty]
internal string Publisher { get; set; }
}


Following is the code I have. But this is not case-sensitive. It is succeeding for both upper and lowercase:

string content = File.ReadAllText(contentFilePath);
JsonSerializerSettings jsonSerializerSettings1 = new JsonSerializerSettings()
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
ResponseList response = (ResponseList)JsonConvert.DeserializeObject(contentResourceOutput, typeof(ResponseList), Constants.JsonSerializerSettings);


The code should only succeed if the content is:

{
"value": [
{
"id": "id1",
"location": "location1",
"plan": {
"name": "free",
"product": "product1",
"publisher": "publisher1"
}
}
]
}


and fail if even if one of the keys is uppercase. E.g.

{
"value": [
{
"Id": "id1",
"Location": "location1",
"plan": {
"Name": "free",
"product": "product1",
"publisher": "publisher1"
}
}
]
}


Notice that only the Keys/Property names should be lower case. The values can be upper case.
Is there a way to make JsonConvert.Deserializeobject case sensitive?

Answer

You can write a custom converter to handle this use case. In regards to your need for a recursive inspection of all key names, I used the fantastic WalkNode answer given by Thymine here.

var json = @"{""id"": ""id1"",""name"": ""name1"",""type"": ""type1""}";
var json2 = @"{""id"": ""id1"",""Name"": ""name1"",""type"": ""type1""}";

JsonSerializerSettings settings = new JsonSerializerSettings()
{
    ContractResolver = new CamelCasePropertyNamesContractResolver(),
    Converters = new List<JsonConverter> { new CamelCaseOnlyConverter() }
};

var response = JsonConvert.DeserializeObject<Response>(json, settings);
var response2 = JsonConvert.DeserializeObject<Response>(json2, settings);

public class CamelCaseOnlyConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, 
        object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;

        var token = (JObject)JToken.Load(reader);

        var isCamelCased = true;
        WalkNode(token, null,
        t =>
        {
            var nameFirstChar = t.Name[0].ToString();
            if (!nameFirstChar.Equals(nameFirstChar.ToLower(), 
                StringComparison.CurrentCulture))
            {
                isCamelCased = false;
                return;
            }
        });

        if (!isCamelCased) return null;

        return token.ToObject(objectType);
    }

    public override void WriteJson(JsonWriter writer, object value, 
        JsonSerializer serializer)
    {
        JObject o = (JObject)JToken.FromObject(value);
        o.WriteTo(writer);
    }

    private static void WalkNode(JToken node,
                            Action<JObject> objectAction = null,
                            Action<JProperty> propertyAction = null)
    { 
        if (node.Type == JTokenType.Object)
        {
            if (objectAction != null) objectAction((JObject)node);
            foreach (JProperty child in node.Children<JProperty>())
            {
                if (propertyAction != null) propertyAction(child);
                WalkNode(child.Value, objectAction, propertyAction);
            }
        }
        else if (node.Type == JTokenType.Array)
            foreach (JToken child in node.Children())
                WalkNode(child, objectAction, propertyAction);
    }
}

The first string will return a hydrated object. The second string will terminate early, returning null.

Comments