sol*_*ish 7 c# json json.net c#-9.0
在 .NET 5.0 上使用 C# 9,我有一堆记录类型,如下所示:
public record SomethingHappenedEvent(Guid Id, object TheThing, string WhatHappened)
{
public SomethingHappenedEvent(object theThing, string whatHappened)
: this(Guid.NewGuid(), theThing, whatHappened)
{ }
}
Run Code Online (Sandbox Code Playgroud)
正如您所料,它们被序列化并发送到其他地方进行处理。发送者调用双参数构造函数并获得一个新的 Id,但反序列化器需要使用记录声明隐含的“主要”3 参数构造函数。我正在使用 Newtonsoft Json.NET,我当然希望这能奏效:
var record = new SomethingHappenedEvent("roof", "caught fire");
var json = JsonConvert.SerializeObject(record);
var otherSideRecord = JsonConvert.DeserializeObject<SomethingHappenedEvent>(json);
Assert.AreEqual(record, otherSideRecord);
Run Code Online (Sandbox Code Playgroud)
当然不是。它抛出 JsonSerializationException。它找不到正确的构造函数,因为有两个,既不是默认的零参数构造函数,也没有标记 JsonConstructorAttribute。我的问题实际上是“我有什么选择来获得类似的东西?”。这会很棒:
[JsonConstructor]
public record SomethingHappenedEvent(Guid Id, object TheThing, string WhatHappened)
{
public SomethingHappenedEvent(object theThing, string whatHappened)
: this(Guid.NewGuid(), theThing, whatHappened)
{ }
}
Run Code Online (Sandbox Code Playgroud)
但这会尝试将该属性应用于无效的类型。这是 C# 中的语法错误,但显然它适用于 F#。
public record SomethingHappenedEvent
[JsonConstructor]
(Guid Id, object TheThing, string WhatHappened)
{
public SomethingHappenedEvent(object theThing, string whatHappened)
: this(Guid.NewGuid(), theThing, whatHappened)
{ }
}
Run Code Online (Sandbox Code Playgroud)
我目前的解决方案是将这些类型保留为普通类,并使用所有额外的样板。我也知道我可以省略自定义构造函数并让我的调用者生成 ID。这是有效的,因为 json.net 只有一个构造函数可供查找。当然是简洁了!但是我不喜欢在所有调用站点上重复代码,即使在这种情况下它很小。
public record SomethingHappenedEvent(Guid Id, object TheThing, string WhatHappened) { }
Run Code Online (Sandbox Code Playgroud)
FWIW 听起来 System.Text.Json 有相同的限制。
AAA*_*ddd 18
首先,您只需在创建自己的构造函数时执行此操作。这是因为在实例化时它不知道要使用哪一个。
其次,请注意(默认情况下)反序列化器将使用属性和构造函数名称,并覆盖您在实际构造函数类型中省略的名称。此外,构造函数中的每个参数必须在反序列化时绑定到对象属性或字段。如果您没有意识到前者可能会导致细微的错误,但这不仅仅限于记录。
除此之外,您将该属性放在了错误的位置。简而言之,属性需要位于构造函数上。
疯狂设计的荒谬的例子:
给定
public record TestRecord(Guid Id)
{
[JsonConstructor]
public TestRecord(object theThing, string whatHappened) : this(Guid.NewGuid())
{
}
}
Run Code Online (Sandbox Code Playgroud)
测试
var record = new TestRecord(Guid.NewGuid());
var json = JsonConvert.SerializeObject(record,Formatting.Indented);
Console.WriteLine(json);
var otherSideRecord = JsonConvert.DeserializeObject<TestRecord>(json);
// note this paradoxically still works, because it has overwritten the ID
Console.WriteLine(record == otherSideRecord);
Run Code Online (Sandbox Code Playgroud)
输出
{
"Id": "2905cfaf-d13d-4df1-af83-e4dcde20d44f"
}
True
Run Code Online (Sandbox Code Playgroud)
请注意,该属性也适用于Text.Json
var json = JsonSerializer.Serialize(record);
var otherSideRecord = JsonSerializer.Deserialize<TestRecord>(json);
Run Code Online (Sandbox Code Playgroud)
我在试验时遇到了另一种选择。我会把它留在这里以防它对某人有用。您可以将自定义构造函数转换为工厂方法。这样就只剩下一个构造函数供解串器查找。
public record SomethingHappenedEvent(Guid Id, object TheThing, string WhatHappened)
{
public static SomethingHappenedEvent Create(object theThing, string whatHappened)
=> new(Guid.NewGuid(), theThing, whatHappened);
}
Run Code Online (Sandbox Code Playgroud)
它改变了调用站点的语法。只需调用 Create 而不是 new:
var myEvent = SomethingHappenedEvent.Create("partyPeople", "gotDown");
Run Code Online (Sandbox Code Playgroud)
当然,您可以将静态工厂方法推送到单独的工厂对象 - 如果您的对象构造具有更丰富的依赖项,这可能会很有用。
| 归档时间: |
|
| 查看次数: |
2710 次 |
| 最近记录: |