json.net custom jobject deserialization

旧城冷巷雨未停 提交于 2019-12-24 01:53:57

问题


I'm trying to use JsonConvert.DeserializeObject(string) to deserialize a string into an jobject that can use with dynamic to access the json document on the fly. However I want to avoid knowing the casing of the document so I can type

dynamic document = JsonConvert.DeserializeObject(someString);
Console.WriteLine(document.some.path.here.name);

and have it work on {"Some":{"path":{"HERE":{"Name":"test"}}} I can't find out how to create a custom class for json.net that will do that for me, basically removing case sensitivity on the jobject (or maybe transform all properties to lower case)


回答1:


To recursively convert all properties in a JToken hierarchy to lower case, you can use the following extension method:

public static class JsonExtensions
{
    public static TJToken RenamePropertiesToLowerInvariant<TJToken>(this TJToken root) where TJToken : JToken
    {
        return root.RenameProperties(s => s.ToLowerInvariant());
    }

    public static TJToken RenameProperties<TJToken>(this TJToken root, Func<string, string> map) where TJToken : JToken
    {
        if (map == null)
            throw new ArgumentNullException();
        if (root == null)
            return null;
        if (root is JProperty)
        {
            return RenameReplaceProperty(root as JProperty, map, -1) as TJToken;
        }
        else
        {
            foreach (IList<JToken> obj in root.DescendantsAndSelf().OfType<JObject>())
                for (int i = obj.Count - 1; i >= 0; i--)
                    RenameReplaceProperty((JProperty)obj[i], map, i);
            return root;
        }
    }

    public static IEnumerable<JToken> DescendantsAndSelf(this JToken node)
    {
        if (node == null)
            return Enumerable.Empty<JToken>();
        var container = node as JContainer;
        if (container != null)
            return container.DescendantsAndSelf();
        else
            return new[] { node };
    }

    private static JProperty RenameReplaceProperty(JProperty property, Func<string, string> map, int index)
    {
        // JProperty.Name is read only so it will need to be replaced in its parent.
        if (property == null)
            return null;
        var newName = map(property.Name);
        if (newName == property.Name)
            return property;
        var value = property.Value;
        // Setting property.Value to null on the old property prevents the child JToken hierarchy from getting recursively cloned when added to the new JProperty
        // See https://github.com/JamesNK/Newtonsoft.Json/issues/633#issuecomment-133358110
        property.Value = null; 
        var newProperty = new JProperty(newName, value);
        IList<JToken> container = property.Parent;
        if (container != null)
        {
            if (index < 0)
                index = container.IndexOf(property);
            container[index] = newProperty;
        }
        return newProperty;
    }
}

Then do

dynamic document = JsonConvert.DeserializeObject<JToken>(someString).RenamePropertiesToLowerInvariant();

However, JObject is case sensitive and provides no constructor to use a case-invariant comparer in its JPropertyKeyedCollection. And JObjectDynamicProxy.TryGetMember() seems to be doing a simple lookup not a case-insensitive search.

So, unless you can get this answer to work, if you need a case-insensitive dynamic object, you could take one of the replacements for ExpandoObject from How to set ExpandoObject's dictionary as case insensitive? then create your own version of ExpandoObjectConverter to deserialize your alternative expando type.



来源:https://stackoverflow.com/questions/42180161/json-net-custom-jobject-deserialization

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!