【问题标题】:Use different name for serializing and deserializing with Json.Net使用不同的名称进行 Json.Net 的序列化和反序列化
【发布时间】:2017-11-21 18:44:53
【问题描述】:

我从如下所示的 Web API 接收 JSON 数据:

[
  {
    "id": 1
    "error_message": "An error has occurred!"
  }
]

我将此数据反序列化为以下类型的对象:

public class ErrorDetails
{
    public int Id { get; set; }

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

稍后在我的应用程序中,我想再次将 ErrorDetails 对象序列化为 JSON,但使用属性名称 ErrorMessage 而不是 error_message。所以结果应该是这样的:

[
  {
    "Id": 1
    "ErrorMessage": "An error has occurred!"
  }
]

有没有一种简单的方法可以使用 Json.Net 完成此任务?也许使用自定义解析器和一些属性,例如:

public class ErrorDetails
{
    public int Id { get; set; }

    [SerializeAs("ErrorMessage")]
    [DeserializeAs("error_message")]
    public string ErrorMessage { get; set; }
}

但是当我在序列化或反序列化时,解析器并没有告诉我。

【问题讨论】:

标签: c# json serialization


【解决方案1】:

您可以使用JsonSerializerSettings、ContractResolver 和 NamingStrategy。

public class ErrorDetails
{
    public int Id { get; set; }
    public string ErrorMessage { get; set; }
}

var json = "{'Id': 1,'error_message': 'An error has occurred!'}";

对于反序列化,您可以使用SnakeCaseNamingStrategy

var dezerializerSettings = new JsonSerializerSettings
{
    ContractResolver = new DefaultContractResolver
    {
        NamingStrategy = new SnakeCaseNamingStrategy()
    }
};
var obj = JsonConvert.DeserializeObject<ErrorDetails>(json, dezerializerSettings);

要再次序列化对象,您不必更改JsonSerializerSettings,因为默认将使用属性名称。

var jsonNew = JsonConvert.SerializeObject(obj);

jsonNew = "{'Id': 1,'ErrorMessage': '发生错误!'}"


或者您可以创建一个可以决定使用哪个名称的合同解析器。然后,您可以决定何时反序列化和序列化是要使用 pascal 大小写名称格式还是带有下划线的格式。

public class CustomContractResolver : DefaultContractResolver
{
    public bool UseJsonPropertyName { get; }

    public CustomContractResolver(bool useJsonPropertyName)
    {
        UseJsonPropertyName = useJsonPropertyName;
    }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        if (!UseJsonPropertyName)
            property.PropertyName = property.UnderlyingName;

        return property;
    }
}

public class ErrorDetails
{
    public int Id { get; set; }
    [JsonProperty("error_message")]
    public string ErrorMessage { get; set; }
}


var json = "{'Id': 1,'error_message': 'An error has occurred!'}";
var serializerSettings = new JsonSerializerSettings()
{
    ContractResolver = new CustomContractResolver(false)
};
var dezerializerSettings = new JsonSerializerSettings
{
    ContractResolver = new CustomContractResolver(true)
};

var obj = JsonConvert.DeserializeObject<ErrorDetails>(json, dezerializerSettings);
var jsonNew = JsonConvert.SerializeObject(obj, serializerSettings);

jsonNew = "{'Id': 1,'ErrorMessage': '发生错误!'}"

【讨论】:

  • 这里要指出的一点是,如果您决定使用CamelCasePropertyNamesContractResolver 作为基础而不是DefaultContractResolver,那么生成的属性名称不会采用驼峰式.因此,您必须手动将名称命名为驼峰式,例如property.UnderlyingName.ToCamleCase()。我们不能直接使用 JSON.Net ToCamelCase() 方法,因为它在内部 StringUtils 类中,因此您必须提供自己的:github.com/JamesNK/Newtonsoft.Json/blob/…
【解决方案2】:

在序列化和反序列化时实现不同属性名称的另一种方法是使用ShouldSerialize 方法:https://www.newtonsoft.com/json/help/html/ConditionalProperties.htm#ShouldSerialize

文档说:

要有条件地序列化一个属性,添加一个返回的方法 与属性同名的布尔值,然后为方法添加前缀 以ShouldSerialize 命名。方法的结果决定是否 该属性已序列化。如果该方法返回 true,则 属性将被序列化,如果它返回 false 则该属性 将被跳过。

例如:

public class ErrorDetails
{
    public int Id { get; set; }

    // This will deserialise the `error_message` property from the incoming json into the `GetErrorMessage` property
    [JsonProperty("error_message")]
    public string GetErrorMessage { get; set; }

    // If this method returns false then the property after the `ShouldSerialize` prefix will not be serialised into the output
    public bool ShouldSerializeGetErrorMessage() => false;

    // The serialised output will return `ErrorMessage` with the value from `GetErrorMessage` i.e. `error_message` in the original json
    public string ErrorMessage { get { return GetErrorMessage; } }
}

这会导致稍微多一点的开销,所以在处理大量属性或大量数据时要小心,但对于小负载,如果您不介意稍微弄乱您的 DTO 类,那么这可能是一个更快的解决方案而不是编写自定义合约解析器等。

【讨论】:

    【解决方案3】:

    我喜欢@lee_mcmullen 的回答,并在我自己的代码中实现了它。现在我想我找到了一个稍微整洁的版本。

    public class ErrorDetails
    {
        public int Id { get; set; }
    
        // This will deserialise the `error_message` property from the incoming json and store it in the new `GetErrorMessage` property
        [JsonProperty("error_message")]
        public string GetErrorMessage { get { return ErrorMessage; } set { ErrorMessage = value; } }
    
        // If this method returns false then the property after the `ShouldSerialize` prefix will not be serialised into the output
        public bool ShouldSerializeGetErrorMessage() => false;
    
        // The serialised output will return `ErrorMessage` with the value set from `GetErrorMessage` i.e. `error_message` in the original json
        public string ErrorMessage { get; set; }
    }
    

    我更喜欢这个的原因是,在更复杂的模型中,它允许继承,同时将所有“旧”自定义内容分开

    public class ErrorDetails
    {
        public int Id { get; set; }
        public string ErrorMessage { get; set; }
    }
    
    // This is our old ErrorDetails that hopefully we can delete one day 
    public class OldErrorDetails : ErrorDetails
    {
        // This will deserialise the `error_message` property from the incoming json and store it in the new `GetErrorMessage` property
        [JsonProperty("error_message")]
        public string GetErrorMessage { get { return ErrorMessage; } set { ErrorMessage = value; } }
    
        // If this method returns false then the property after the `ShouldSerialize` prefix will not be serialised into the output
        public bool ShouldSerializeGetErrorMessage() => false;
    }
    

    【讨论】:

    • 这个非常好的答案,虽然它在代码中有一些开销,但它确实会产生良好的 JSON 结果。不错!
    猜你喜欢
    • 2014-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-11
    • 1970-01-01
    • 2017-11-29
    • 2013-10-09
    相关资源
    最近更新 更多