Json。NET反序列化或序列化json字符串,并将属性映射到运行时定义的不同属性名

时间:2021-03-29 17:02:07

I have the following JSON string:

我有以下JSON字符串:

{
    "values": {
        "details": {
            "property1": "94",
            "property2": "47",
            "property3": "32",
            "property4": 1
        },
        count: 4
    }
}     

I am going to map this to the following model:

我将把它映射到以下模型:

public class Details
{
    public string property1 { get; set; }
    public string property2 { get; set; }
    public string property3 { get; set; }
    public int property4 { get; set; }
}

public class Values
{
    public Details details { get; set; }
    public int count { get; set; }
}

public class RootObject
{
    public Values values { get; set; }
}

I want to be able to map the these property names to different names at runtime when deserializing this JSON string like this:

在运行时反序列化这个JSON字符串时,我希望能够将这些属性名映射到不同的名称:

JsonConvert.DeserializeObject<RootObject>(jsonString);

For example, in the deserialization process, I want the deserialize the name of "property1" to "differen_property_name1" or "differen_property_name2" or "differen_property_name3". Because I am choosing the new name at runtime (the new name to which I will change the "property1" name to), I can't use the solution using JsonPropertyAttribute, as suggested here:

例如,在反序列化过程中,我希望将“property1”的名称反序列化为“差异_property_name1”或“差异_property_name2”或“差异_property_name3”。因为我在运行时选择了新名称(我将把“property1”名称改为的新名称),所以不能使用JsonPropertyAttribute解决方案,如这里所建议的:

.NET NewtonSoft JSON deserialize map to a different property name

.NET NewtonSoft JSON反序列化映射到另一个属性名

One of the answers of the above question (Jack's answer) uses inheritance of DefaultContractResolver but it doesn't seem to work in that case.

上面问题的一个答案(Jack的答案)使用了defaultcontractcontractresolver的继承,但在这种情况下似乎行不通。

Update

更新

Later on, I needed to serialize the object I got from the deserialization and map the properties to different property names, defined at runtime. I used the same method as Brian proposed to do the serialization:

稍后,我需要序列化从反序列化得到的对象,并将属性映射到在运行时定义的不同属性名。我使用和Brian建议的方法进行序列化:

I used the dictionary to map my new property names:

我用字典来映射我的新属性名:

var map = new Dictionary<Type, Dictionary<string, string>>
{
    { 
        typeof(Details), 
        new Dictionary<string, string>
        {
            {"property1", "myNewPropertyName1"},
            {"property2", "myNewPropertyName2"},
            {"property3", "myNewPropertyName3"},
            {"property4", "myNewPropertyName4"}
        }
    }
};    

and then I used Brian's DynamicMappingResolver to serialize the object like this:

然后我用Brian的DynamicMappingResolver来序列化这个对象

var settings = new JsonSerializerSettings
{
    ContractResolver = new DynamicMappingResolver(map)
};

var root = JsonConvert.SerializeObject(myObjectInstance, settings);            

4 个解决方案

#1


7  

You could use a custom ContractResolver to do this. Basically it is the same idea as putting a [JsonProperty] attribute on each class member for which you want to map to a different JSON property name, except you do it programmatically via the resolver. You can pass a dictionary of your desired mappings to the resolver when setting it up just before deserializing.

您可以使用自定义的契约解析器来完成此操作。基本上,这与在每个要映射到不同JSON属性名的类成员上放置[JsonProperty]属性是相同的思想,只不过是通过解析器编程实现的。您可以在反序列化之前设置resolver时将所需映射的字典传递给它。

Here is what the custom resolver code might look like:

下面是自定义解析器代码的外观:

class DynamicMappingResolver : DefaultContractResolver
{
    private Dictionary<Type, Dictionary<string, string>> memberNameToJsonNameMap;

    public DynamicMappingResolver(Dictionary<Type, Dictionary<string, string>> memberNameToJsonNameMap)
    {
        this.memberNameToJsonNameMap = memberNameToJsonNameMap;
    }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty prop = base.CreateProperty(member, memberSerialization);
        Dictionary<string, string> dict;
        string jsonName;
        if (memberNameToJsonNameMap.TryGetValue(member.DeclaringType, out dict) && 
            dict.TryGetValue(member.Name, out jsonName))
        {
            prop.PropertyName = jsonName;
        }
        return prop;
    }
}

To use the resolver, first construct a Dictionary<Type, Dictionary<string, string>> containing your mappings. The outer dictionary's key is the the class type(s) whose properties you want to map; the inner dictionary is a mapping of the class property names to JSON property names. You only need to provide a mapping for the properties whose names don't already match the JSON.

要使用解析器,首先构造一个字典 >包含映射。外部字典的键是类类型,它的属性是您想要映射的;内部字典是类属性名称到JSON属性名称的映射。只需为名称与JSON不匹配的属性提供映射。 ,>

So, for example, if your JSON looked like this (notice the changed names of the properties inside the details object)...

因此,例如,如果您的JSON看起来是这样的(请注意细节对象中属性名称的更改)……

{
    "values": {
        "details": {
            "foo": "94",
            "bar": "47",
            "baz": "32",
            "quux": 1
        },
        count: 4
    }
}

...and you wanted to map it to the classes in your question, you would create the dictionary like this:

…你想把它映射到你的问题中的类,你会创建这样的字典:

var map = new Dictionary<Type, Dictionary<string, string>>
{
    { 
        typeof(Details), 
        new Dictionary<string, string>
        {
            {"property1", "foo"},
            {"property2", "bar"},
            {"property3", "baz"},
            {"property4", "quux"}
        }
    }
};

The last step is to set up the serializer settings with a new resolver instance, giving it the mapping dictionary you just constructed, and then pass the settings to JsonConvert.DeserializeObject().

最后一步是使用一个新的解析器实例来设置序列化器设置,为它提供刚才构造的映射字典,然后将设置传递给JsonConvert.DeserializeObject()。

var settings = new JsonSerializerSettings
{
    ContractResolver = new DynamicMappingResolver(map)
};

var root = JsonConvert.DeserializeObject<RootObject>(json, settings);

Here is a demo: https://dotnetfiddle.net/ULkB0J

这里有一个演示:https://dotnetfiddle.net/ULkB0J

#2


1  

Why do this in one step? Why not deserialize into your standard object and then map them over dynamically using Automapper?

为什么要一步一步来?为什么不反序列化到标准对象中,然后使用Automapper动态映射它们呢?

something like:

喜欢的东西:

Mapper.Initialize(c =>
{
    c.ReplaceMemberName("property1 ", "differen_property_name1");
});

#3


1  

If you don't want to use a custom ContractResolver to do this. Use [JsonProperty("")] to look for different variations of the property name and return with another property like this:

如果您不想使用自定义的契约解析器来完成此任务,请使用它。使用[JsonProperty(")]来查找属性名称的不同变体,并返回如下所示的另一个属性:

public class Details
{
    private string _property1;
    private string _property2;

    [JsonProperty("property1")]
    public string prop1 {get;set;}

    [JsonProperty("foo")]
    public string foo {get;set;}

    public string getProperty1 
    {
        get {_property1=prop1??foo;return _property1;}
        set{prop1=value;foo=value;}
    }

    [JsonProperty("property2")]
    public string prop2 {get;set;}

    [JsonProperty("bar")]
    public string bar {get;set;}

    public string getProperty2
    {
        get {_property2=prop2??bar;return _property2;}
        set {prop2=value;bar=value;}
    }
}

Demo here: https://dotnetfiddle.net/V17igc

演示:https://dotnetfiddle.net/V17igc

#4


0  

I don't believe that JSON.net has any support for what you are looking for. You should instead deserialize the JSON into JObject if you don't know the format or some generic one if you do (for instance if the JSON always says property1 you can use a generic object to represent it).

我不相信JSON.net对您正在寻找的东西有任何支持。相反,如果不知道格式或通用格式,则应该将JSON反序列化为JObject(例如,如果JSON总是说property1,则可以使用通用对象来表示)。

Once you have your generic object then next you need to translate the fields. Any that aren't changeable can be done directly, but for anything else you will need to use Reflection.

一旦有了泛型对象,接下来就需要转换字段。任何不可更改的都可以直接执行,但是对于其他任何事情,您都需要使用反射。

Basically it involves getting the type (typeof(Details) or obj.GetType()) and then searching for the Property you want to update. Finally you should be able to find the setter method and call it supplying the original value out of your generic object.

基本上,它包括获取类型(类型(细节)或object . gettype()),然后搜索要更新的属性。最后,您应该能够找到setter方法,并调用它从泛型对象中提供原始值。

#1


7  

You could use a custom ContractResolver to do this. Basically it is the same idea as putting a [JsonProperty] attribute on each class member for which you want to map to a different JSON property name, except you do it programmatically via the resolver. You can pass a dictionary of your desired mappings to the resolver when setting it up just before deserializing.

您可以使用自定义的契约解析器来完成此操作。基本上,这与在每个要映射到不同JSON属性名的类成员上放置[JsonProperty]属性是相同的思想,只不过是通过解析器编程实现的。您可以在反序列化之前设置resolver时将所需映射的字典传递给它。

Here is what the custom resolver code might look like:

下面是自定义解析器代码的外观:

class DynamicMappingResolver : DefaultContractResolver
{
    private Dictionary<Type, Dictionary<string, string>> memberNameToJsonNameMap;

    public DynamicMappingResolver(Dictionary<Type, Dictionary<string, string>> memberNameToJsonNameMap)
    {
        this.memberNameToJsonNameMap = memberNameToJsonNameMap;
    }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty prop = base.CreateProperty(member, memberSerialization);
        Dictionary<string, string> dict;
        string jsonName;
        if (memberNameToJsonNameMap.TryGetValue(member.DeclaringType, out dict) && 
            dict.TryGetValue(member.Name, out jsonName))
        {
            prop.PropertyName = jsonName;
        }
        return prop;
    }
}

To use the resolver, first construct a Dictionary<Type, Dictionary<string, string>> containing your mappings. The outer dictionary's key is the the class type(s) whose properties you want to map; the inner dictionary is a mapping of the class property names to JSON property names. You only need to provide a mapping for the properties whose names don't already match the JSON.

要使用解析器,首先构造一个字典 >包含映射。外部字典的键是类类型,它的属性是您想要映射的;内部字典是类属性名称到JSON属性名称的映射。只需为名称与JSON不匹配的属性提供映射。 ,>

So, for example, if your JSON looked like this (notice the changed names of the properties inside the details object)...

因此,例如,如果您的JSON看起来是这样的(请注意细节对象中属性名称的更改)……

{
    "values": {
        "details": {
            "foo": "94",
            "bar": "47",
            "baz": "32",
            "quux": 1
        },
        count: 4
    }
}

...and you wanted to map it to the classes in your question, you would create the dictionary like this:

…你想把它映射到你的问题中的类,你会创建这样的字典:

var map = new Dictionary<Type, Dictionary<string, string>>
{
    { 
        typeof(Details), 
        new Dictionary<string, string>
        {
            {"property1", "foo"},
            {"property2", "bar"},
            {"property3", "baz"},
            {"property4", "quux"}
        }
    }
};

The last step is to set up the serializer settings with a new resolver instance, giving it the mapping dictionary you just constructed, and then pass the settings to JsonConvert.DeserializeObject().

最后一步是使用一个新的解析器实例来设置序列化器设置,为它提供刚才构造的映射字典,然后将设置传递给JsonConvert.DeserializeObject()。

var settings = new JsonSerializerSettings
{
    ContractResolver = new DynamicMappingResolver(map)
};

var root = JsonConvert.DeserializeObject<RootObject>(json, settings);

Here is a demo: https://dotnetfiddle.net/ULkB0J

这里有一个演示:https://dotnetfiddle.net/ULkB0J

#2


1  

Why do this in one step? Why not deserialize into your standard object and then map them over dynamically using Automapper?

为什么要一步一步来?为什么不反序列化到标准对象中,然后使用Automapper动态映射它们呢?

something like:

喜欢的东西:

Mapper.Initialize(c =>
{
    c.ReplaceMemberName("property1 ", "differen_property_name1");
});

#3


1  

If you don't want to use a custom ContractResolver to do this. Use [JsonProperty("")] to look for different variations of the property name and return with another property like this:

如果您不想使用自定义的契约解析器来完成此任务,请使用它。使用[JsonProperty(")]来查找属性名称的不同变体,并返回如下所示的另一个属性:

public class Details
{
    private string _property1;
    private string _property2;

    [JsonProperty("property1")]
    public string prop1 {get;set;}

    [JsonProperty("foo")]
    public string foo {get;set;}

    public string getProperty1 
    {
        get {_property1=prop1??foo;return _property1;}
        set{prop1=value;foo=value;}
    }

    [JsonProperty("property2")]
    public string prop2 {get;set;}

    [JsonProperty("bar")]
    public string bar {get;set;}

    public string getProperty2
    {
        get {_property2=prop2??bar;return _property2;}
        set {prop2=value;bar=value;}
    }
}

Demo here: https://dotnetfiddle.net/V17igc

演示:https://dotnetfiddle.net/V17igc

#4


0  

I don't believe that JSON.net has any support for what you are looking for. You should instead deserialize the JSON into JObject if you don't know the format or some generic one if you do (for instance if the JSON always says property1 you can use a generic object to represent it).

我不相信JSON.net对您正在寻找的东西有任何支持。相反,如果不知道格式或通用格式,则应该将JSON反序列化为JObject(例如,如果JSON总是说property1,则可以使用通用对象来表示)。

Once you have your generic object then next you need to translate the fields. Any that aren't changeable can be done directly, but for anything else you will need to use Reflection.

一旦有了泛型对象,接下来就需要转换字段。任何不可更改的都可以直接执行,但是对于其他任何事情,您都需要使用反射。

Basically it involves getting the type (typeof(Details) or obj.GetType()) and then searching for the Property you want to update. Finally you should be able to find the setter method and call it supplying the original value out of your generic object.

基本上,它包括获取类型(类型(细节)或object . gettype()),然后搜索要更新的属性。最后,您应该能够找到setter方法,并调用它从泛型对象中提供原始值。