概述
我正在尝试使用ASP.NET Core编写Web服务,允许客户端查询和修改微控制器的状态 . 该微控制器包含我在我的应用中建模的许多系统 - 例如,PWM系统, Actuator 输入系统等 .
这些系统的组件都具有可以使用JSON patch请求查询或修改的特定属性 . 例如,可以使用携带 {"op":"replace", "path":"/pwms/3/enabled", "value":true}
的HTTP请求启用micro上的第4个PWM . 为了支持这一点,我正在使用AspNetCore.JsonPatch库 .
My problem 是我'm trying to implement JSON Patch support for a new 1046257 system that logically should map a definition name to a particular CAN message definition, and I'我不知道该如何解决这个问题 .
详情
下图为CAN数据库系统建模 . CanDatabase
实例应该在逻辑上包含 IDictionary<string, CanMessageDefinition>
形式的字典 .
为了支持创建新的消息定义,我的应用程序应该允许用户发送JSON补丁请求,如下所示:
{
"op": "add",
"path": "/candb/my_new_definition",
"value": {
"template": ["...", "..."],
"repeatRate": "...",
"...": "...",
}
}
这里, my_new_definition
将定义 name 定义,并且与 value
关联的对象应该反序列化为 CanMessageDefinition
object . 然后应将其存储为 CanDatabase
字典中的新键值对 .
问题是 path
应指定一个 property path ,对于静态类型的对象,它将是......好的,静态的(例外情况是它允许引用数组元素,例如 /pwms/3
,如上所述) .
我尝试过的
A. The Leeroy Jenkins approach
忘记我知道它不起作用的事实 - 我尝试了下面的实现(尽管我需要支持动态JSON补丁路径,但它只使用静态类型)只是为了看看会发生什么 .
Implementation
internal sealed class CanDatabaseModel : DeviceComponentModel<CanDatabaseModel>
{
public CanDatabaseModel()
{
this.Definitions = new Dictionary<string, CanMessageDefinition>();
}
[JsonProperty(PropertyName = "candb")]
public IDictionary<string, CanMessageDefinition> Definitions { get; }
...
}
Test
{
"op": "add",
"path": "/candb/foo",
"value": {
"messageId": 171,
"template": [17, 34],
"repeatRate": 100,
"canPort": 0
}
}
Outcome
在我尝试将指定的更改应用于 JsonPatchDocument
的站点上抛出 InvalidCastException
.
现场:
var currentModelSnapshot = this.currentModelFilter(this.currentModel.Copy());
var snapshotWithChangesApplied = currentModelSnapshot.Copy();
diffDocument.ApplyTo(snapshotWithChangesApplied);
例外:
Unable to cast object of type 'Newtonsoft.Json.Serialization.JsonDictionaryContract' to type 'Newtonsoft.Json.Serialization.JsonObjectContract'.
B. Relying on dynamic JSON Patching
一个更有希望的攻击计划似乎依赖于dynamic JSON patching,其涉及对 ExpandoObject
的实例执行补丁操作 . 这允许您使用JSON补丁文档来添加,删除或替换属性,因为您正在处理动态类型的对象 .
Implementation
internal sealed class CanDatabaseModel : DeviceComponentModel<CanDatabaseModel>
{
public CanDatabaseModel()
{
this.Definitions = new ExpandoObject();
}
[JsonProperty(PropertyName = "candb")]
public IDictionary<string, object> Definitions { get; }
...
}
Test
{
"op": "add",
"path": "/candb/foo",
"value": {
"messageId": 171,
"template": [17, 34],
"repeatRate": 100,
"canPort": 0
}
}
Outcome
进行此更改允许我的测试的这一部分在没有引发异常的情况下运行,但是JSON Patch不知道要将 value
反序列化为什么,导致数据作为 JObject
而不是 CanMessageDefinition
存储在字典中:
是否有可能_1146294_ JSON补丁如何通过任何机会反序列化信息?也许是在 Definitions
上使用 JsonConverter
属性的行为?
[JsonProperty(PropertyName = "candb")]
[JsonConverter(...)]
public IDictionary<string, object> Definitions { get; }
摘要
-
我需要支持向字典添加值的JSON补丁请求
-
我试过走纯粹的静态路线,但失败了
-
我尝试过使用动态JSON补丁
-
这部分有效,但我的数据存储为
JObject
类型而不是预期的类型 -
是否有一个属性(或其他技术)我可以应用于我的属性,让它反序列化为正确的类型(不是匿名类型)?
1 回答
由于似乎没有任何官方方法可以做到这一点,我想出了一个临时解决方案™(阅读:一个运行良好的解决方案,所以我可能永远保留它) .
为了使它看起来像JSON Patch处理类似字典的操作,我创建了一个名为
DynamicDeserialisationStore
的类,它继承自DynamicObject并利用JSON Patch对动态对象的支持 .更具体地说,这个类重写像
TrySetMember
,TrySetIndex
,TryGetMember
等方法基本上像字典一样,除了它将所有这些操作委托给提供给它的构造函数的回调 .Implementation
下面的代码提供了
DynamicDeserialisationStore
的实现 . 它实现IDictionary<string, object>
(这是签名JSON补丁需要使用动态对象),但我只实现了我需要的最少的方法 .JSON Patch对动态对象的支持问题在于它将属性设置为
JObject
实例,即它赢得了't automatically perform deserialisation like it would when setting static properties, as it can' t推断类型 .DynamicDeserialisationStore
参数化对象的类型,它将尝试自动尝试将这些JObject
实例反序列化为它们的设置 .该类接受回调来处理基本字典操作,而不是维护内部字典本身,因为在我的“真实”系统模型代码中,我实际上并没有使用字典(出于各种原因) - 我只是让它看起来像是通向客户端 .
Tests
下面提供了这个类的测试 . 我创建了一个模拟系统模型(见图)并在其上执行各种JSON补丁操作 .
这是代码: