我差点解决了我的问题,但错过了最后一块......
我有一个对象列表,我希望能够添加值类型(通常将序列化为字符串)并将它们作为原始类型返回。 例如: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?
感谢。
答案 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进行愉快而身临其境的分析。
我知道,我的英语非常糟糕:)