json.net ExtensionData在反序列化时修改json密钥名称

时间:2016-03-09 07:15:24

标签: c# json.net deserialization

我使用JsonExtensionData属性和Dictionary<string, object>,这对我所有的&#34;未知&#34;实际上非常有效。 JSON结果。

不幸的是,我从我的webservice获得的JSON字符串有时会给我带有非字母数字字符的键,我想在反序列化时清理它。

例如:

"dc:title":"My Document title"

在反序列化期间,我想删除所有非字母数字字符,我想知道是否有一种简单的方法可以使用自定义转换器?

Derserializer Code

public class TikaDeserializer : IDeserializer
{
    private Newtonsoft.Json.JsonSerializer serializer;


    public TikaDeserializer(Newtonsoft.Json.JsonSerializer serializer)
    {
        this.serializer = serializer;
    }

    public T Deserialize<T>(RestSharp.IRestResponse response)
    {
        var content = response.Content;

        using(var stringReader = new StringReader(content))
        {
            using(var jsonTextReader = new JsonTextReader(stringReader))
            {
                return serializer.Deserialize<T>(jsonTextReader);
            }
        }
    }

    public string DateFormat { get; set; }

    public string Namespace { get; set; }

    public string RootElement { get; set; }

    public static TikaDeserializer Default
    {
        get
        {
            return new TikaDeserializer(new Newtonsoft.Json.JsonSerializer()
            {
                NullValueHandling = NullValueHandling.Ignore,
            });
        }
    }

Gediminas的转换器代码

public class InputKeyNameCleanerConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(Dictionary<,>);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var jObject = JToken.ReadFrom(reader);
        foreach(JToken token in jObject.ToList())
        {
            string replacedName = Regex.Replace(token.Path, @"[^A-Za-z]", String.Empty);
            JProperty newToken = new JProperty(replacedName, token.First);
            token.Replace(newToken);
        }
        return jObject.ToObject(objectType);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotSupportedException();
    }
}

模型

public class ParsedDocument
{

    [JsonProperty("Author")]
    public string Author { get; set; }
    [JsonProperty("Content-Type")]
    public string ContentType { get; set; }
    [JsonProperty("Content-Encoding")]
    public string ContentEncoding { get; set; }
    [JsonProperty("Creation-Date")]
    public DateTime? DateCreated { get; set; }
    [JsonProperty("Last-Modified")]
    public DateTime? DateModified { get; set; }
    [JsonProperty("Last-Save-Date")]
    public DateTime? DateSaved { get; set; }
    [JsonProperty("Last-Printed")]
    public DateTime? DatePrinted { get; set; }

    [JsonProperty("title")]
    public string Title { get; set; }
    [JsonProperty("X-TIKA:content")]
    public string Content { get; set; }

    [JsonExtensionData]
    public Dictionary<string, object> MetaData { get; set; }




}

2 个答案:

答案 0 :(得分:1)

您可以使用替换custom contract resolver代表的JsonObjectContract.ExtensionDataSetter执行此操作:

public class ExtensionNameMappingContractResolver : IContractResolver
{
    readonly IContractResolver baseResolver;
    readonly Regex regex;
    readonly string replacement;

    // As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons.
    // http://www.newtonsoft.com/json/help/html/ContractResolver.htm
    // http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm
    // "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance."

    static ExtensionNameMappingContractResolver removeNonAlphanumericCharactersInstance;

    static ExtensionNameMappingContractResolver()
    {
        // Regex is from https://stackoverflow.com/questions/8779189/how-do-i-strip-non-alphanumeric-characters-including-spaces-from-a-string
        removeNonAlphanumericCharactersInstance = new ExtensionNameMappingContractResolver(new DefaultContractResolver(), new Regex(@"[^\p{L}\p{N}]+"), "");
    }

    public static ExtensionNameMappingContractResolver RemoveNonAlphanumericCharactersInstance { get { return removeNonAlphanumericCharactersInstance; } }

    public ExtensionNameMappingContractResolver(IContractResolver baseResolver, Regex regex, string replacement)
    {
        if (regex == null || replacement == null || baseResolver == null)
            throw new ArgumentNullException();
        this.regex = regex;
        this.replacement = replacement;
        this.baseResolver = baseResolver;
    }

    #region IContractResolver Members

    public JsonContract ResolveContract(Type type)
    {
        var contract = baseResolver.ResolveContract(type);
        if (contract is JsonObjectContract)
        {
            var objContract = (JsonObjectContract)contract;
            if (objContract.ExtensionDataSetter != null)
            {
                var oldSetter = objContract.ExtensionDataSetter;
                objContract.ExtensionDataSetter = (o, key, value) =>
                {
                    var newKey = regex.Replace(key, replacement);
                    oldSetter(o, newKey, value);
                };
            }
        }
        return contract;
    }

    #endregion
}

然后使用它:

        var settings = new JsonSerializerSettings 
        {
             NullValueHandling = NullValueHandling.Ignore,
            ContractResolver = ExtensionNameMappingContractResolver.RemoveNonAlphanumericCharactersInstance 
        };
        var serializer = JsonSerializer.CreateDefault(settings);

请注意,我使用的Regex会替换table of Unicode character categories定义的所有 Unicode 非字母数字字符。有关更多选项,包括删除所有非ASCII,非字母数字字符,请参阅How do I strip non-alphanumeric characters (including spaces) from a string?

请注意,如果通过从属性名称中删除非字母数字字符,合同解析程序会尝试添加重复的扩展数据键,则会引发JsonSerializationException: Error setting value in extension data ---> System.ArgumentException: An item with the same key has already been added

答案 1 :(得分:0)

当然!你可以使用正则表达式。您可以使用[^a-zA-Z0-9]匹配任何非字母数字字符,并将其替换为空字符串。在JsonConverter中,您可以创建一个JObject并对其属性进行迭代,将其替换为具有更正名称的属性。

我提出了一个通用解决方案,它接受一个Regex对象和一个replacement字符串作为构造函数参数:

<强>实施

class DictionaryRegexReplaceJsonConverter : JsonConverter
{
    public Regex ReplacingRegex { get; set; }
    public string Replacement { get; set; }

    public DictionaryRegexReplaceJsonConverter(Regex replacingRegex, string replacement = "")
    {
        ReplacingRegex = replacingRegex;
        Replacement = replacement;
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(Dictionary<,>);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var jo = JToken.ReadFrom(reader);
        foreach (JToken token in jo.ToList())
        {
            string replacedName = ReplacingRegex.Replace(token.Path, Replacement);
            JProperty newToken = new JProperty(replacedName, token.First);
            token.Replace(newToken);
        }
        return jo.ToObject(objectType);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotSupportedException();
    }
}

<强>用法:

Regex regex = new Regex("[^a-zA-Z0-9]");
var converter = new DictionaryKeyReplacingJsonConverter(regex);
string json = "{\"dc:title\":\"My Document title\", \"Another Example!\": \"foo\"}";

// {"dctitle": "My Document title", "AnotherExample": "foo"} ]
var obj = JsonConvert.DeserializeObject<Dictionary<string, object>>(json, converter);

如果关注性能,可以通过手动读取JsonReader并形成对象来优化,而不是使用JObject和替换属性。