【问题标题】:Serialize json value types with type data使用类型数据序列化 json 值类型
【发布时间】:2018-01-31 18:34:40
【问题描述】:

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

我有一个对象列表,我希望能够添加值类型(通常会被序列化为字符串)并将它们恢复为原始类型。 例如: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();
    }

这是序列化/反序列化的示例代码:

    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?

谢谢。

【问题讨论】:

  • 什么是JsonConverter?它来自什么命名空间、库等?
  • 对不起。 Json.Net (Newtonsoft)

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


【解决方案1】:

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

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

更深入

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

相反,ExtString 是使用 JsonObjectContract 解析的,它调用 ExtString 的无参数构造函数(即使您不声明它也存在,因为它是值类型),然后分配属性值到相应的 json 属性值。但是 ExtString 结构属性名称是 Value 而 JSON 属性名称是 $value。因此创建了一个新的ExtString 实例,但Value 属性仍然为空,因为没有名称为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 参数将为空,因此您的Value 属性也是。

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

这样想:

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

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

我认为第一个解决方案可以让您更好地控制设置值,因为(修改 setter 方法)您的逻辑将始终被调用,无论您如何创建对象或何时分配值。

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

我知道,我的英语很差:)

【讨论】:

  • 你的英语很好 :) 听起来很合理,我理解这个问题,甚至找到了另一个解决方案:在序列化时编写构造 var 的名称而不是 $value。不幸的是,我遇到了另一个问题:我还在注册 TypeConverters 以处理与字符串之间的转换(MVC 模型绑定所需),而 Json.Net 正在以某种方式检查它并抛出一个错误,请求将这些类型序列化为字符串!我在这里想完全错了吗?我想要做的就是能够使用值类型对象(例如 HtmlString)对 List 进行序列化/反序列化.... :(
  • Closer :) :找到了 JsonObjectAttribute,它在 ser/deser 时强制将对象作为一个复杂的类处理,并为我自己的类解决了它,但是我该怎么装饰例如。具有此属性的 Guid 或 HtmlString?或者是否存在系统值类型的另一种解决方案?日期工作正常,但我猜那是因为内置转换器/处理......
  • 建议你看看JsonConverterReadJson方法。请参阅此答案:stackoverflow.com/a/8031283/7772490。使用ReadJson,您可以完全控制反序列化。希望对您有所帮助。
  • 我会看看你的想法,但据我所知,ReadJson 在序列化为 Object 时不起作用,因为 CanConvert(Type objectType) 会给你 objectType == Object 和所有类型可以转换为对象,因此您最终会得到一个需要处理所有类型的转换器!请告诉我我错了,因为这会解决很多问题:)!
  • CanConvert 方法中,可以检查对象的当前类型,而不是该类型是否可以转换为另一种类型。尝试将return objectType.IsValueType || objectType == typeof(object); 放入CanConvert 方法中,然后在ReadJson 方法中使用JObject obj = JObject.Load(reader);ReadJson 方法应该只使用对象而不是 List 调用。所以你可以使用obj["$type"]obj["$value"]访问$type$value
猜你喜欢
  • 2017-05-07
  • 1970-01-01
  • 2015-03-23
  • 2016-04-07
  • 2023-03-31
  • 1970-01-01
  • 2021-06-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多