使用不同的名称来使用Json.Net进行序列化和反序列化

时间:2017-06-19 13:52:03

标签: c# json serialization

我从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; }
}

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

3 个答案:

答案 0 :(得分:6)

您可以使用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!'}";

对于dezerialization,您可以使用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案例名称格式或带有下划线的格式,您可以决定何时进行dezerialize和序列化。

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':'发生错误!'}”

答案 1 :(得分:1)

序列化与反序列化时,获得不同属性名称的另一种方法是使用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类,那么这可能是一种更快的解决方案而不是编写自定义合同解析器等。

答案 2 :(得分:0)

我喜欢@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;
}