使用类型数据序列化json值类型

时间:2018-01-31 18:34:41

标签: c# .net json serialization json.net

我差点解决了我的问题,但错过了最后一块......

我有一个对象列表,我希望能够添加值类型(通常将序列化为字符串)并将它们作为原始类型返回。 例如:Guids或自定义值类型。

这是一个示例自定义值类型:

public struct ExtString
{
    private String Value
    {
        get;
        set;
    }

    public static implicit operator ExtString(String str)
    {
        return !String.IsNullOrWhiteSpace(str) ? new ExtString(str) : null;
    }

    public static implicit operator String(ExtString exStr)
    {
        return exStr.ToString();
    }

    public ExtString(String str)
    {
        this.Value = str;
    }

    public override String ToString()
    {
        return this.Value;
    }
}

这是自定义转换器:

public class CustomConverter : JsonConverter
{
    public override Boolean CanConvert(Type objectType)
    {
        return objectType.IsValueType;
    }

    public override bool CanRead => false;

    public override void WriteJson(JsonWriter writer, Object value, JsonSerializer serializer)
    {
        writer.WriteStartObject();

        writer.WritePropertyName("$type");
        writer.WriteValue(value.GetType().AssemblyQualifiedName);

        writer.WritePropertyName("$value");
        writer.WriteValue(value.ToString());

        writer.WriteEndObject();
    }

以下是序列化/ deserilizing的示例代码:

    var jsonSerializerSettings = new JsonSerializerSettings()
    {
        TypeNameHandling = TypeNameHandling.All,
        Converters = new JsonConverter[]
        {
            new CustomConverter()
        }
    };

    var list = new List<Object>();

    list.Add(Guid.NewGuid());
    list.Add((ExtString)"Hello World");

    var ser = JsonConvert.SerializeObject(list, Formatting.Indented, jsonSerializerSettings);

    var deser = JsonConvert.DeserializeObject(ser, jsonSerializerSettings);

这几乎一直都有效... Guid和ExtString都是序列化正确的,Guid甚至用正确的值反序列化(没有特殊处理!),并且ExtString被创建正确(在反序列化时)但是带有值null(调用构造函数但str为null)。

我错过了什么?为什么它适用于Guid?

感谢。

1 个答案:

答案 0 :(得分:0)

您所要做的就是为Value属性指定JSON属性名称。

public struct ExtString
{
    [JsonProperty("$value")]
    private String Value
    {
     ...

更深入

Guid映射有效,因为它被视为primitive type,而Json.Net知道如何创建新实例以及要传递给的参数(使用JsonPrimitiveContract)。

相反,使用调用ExtString的无参数构造函数的JsonObjectContract来解析ExtString(即使您没有声明它也会存在,因为它是值类型)然后将属性值分配给相应的json属性值。但ExtString struct属性名称为Value,而JSON属性名称为$value。因此,创建了ExtString的新范围,但Value属性保持为null,因为没有名称为Value的属性。这就是为什么在您的代码中,ExtString的新属性Value属性设置为null

在上面的解决方案中,我将ExtString结构的属性名称与输入JSON中的属性名称相匹配。创建ExtString结构的实例后,$value属性的映射将成功完成。

另一种解决方案可能是:

public struct ExtString
{
    private String Value
    {
        get;
        set;
    }

    [JsonConstructor]
    public ExtString([JsonProperty("$value")] String str)
    {
        this.Value = str;
    }

在这种情况下,将使用具有JsonConstructor属性的构造函数而不是无参数的构造函数。请注意,必须为str参数提供相应的JsonProperty属性,该属性定义属性名称(在输入JSON中),其值将传递给构造函数。如果省略JsonProperty属性,则str参数将为null,因此您的Value属性也将为空。

两个解决方案之间的区别在于 分配了Value属性。在第一个解决方案中,属性在创建对象后分配,在第二个解决方案中,属性由构造函数分配。

以这种方式思考:

//First solution
var myObject = new ExtString();
myObject.Value = "Hello World";

//Second solution
var myObject = new ExtString("Hello World");

我认为第一个解决方案为您提供了更多控制权来设置值,因为(修改setter方法)无论您如何创建对象或分配值,都会始终调用您的逻辑。

来源:对source code进行愉快而身临其境的分析。

我知道,我的英语非常糟糕:)