我试图将一个复杂的嵌套json转换为我的对象。唯一的问题是,在我的对象中,我使用抽象类型,所以它必须有一些逻辑来使用正确的派生类。
类型保存在枚举中。
为了解释它,我将保持它相当简单,我们只嵌套一次,但为了我的目的,它更多地嵌套对象和类型。
JSON
{
"screen":{
"type":"Component",
"footer":{
"type":"Bar"
},
"header":{
"type":"Top"
}
}
}
字符串
类
public abstract class Screen
{
public abstract ScreenType Type { get; }
}
public enum ScreenType
{
Component,
b,
c,
d,
e
}
public sealed class ComponentScreen : Screen
{
public override ScreenType Type => ScreenType.Component;
public Header? Header { get; init; }
public Footer? Footer { get; init; }
public bool? ShowStuff {get; init; }
}
public abstract class Header : ITyped<HeaderType>
{
public abstract HeaderType Type { get; }
}
public enum HeaderType
{
Top,
b,
c,
d
}
public sealed class TopScreenHeader : Header
{
public override HeaderType Type => HeaderType.Top;
public string MyStuff { get; }
}
型
不可能只改变所有的抽象类型,或者编写转换器,因为有多个抽象类型的X次派生对象。JSON也不一致。
我当前使用newtonsoft的代码
var screen = JsonConvert.DeserializeObject<Screen>(jsonString, new JsonSerializerSettings {
TypeNameHandling = TypeNameHandling.Objects,
ContractResolver = new CamelCasePropertyNamesContractResolver()
}
型
这不工作,并给出错误:
Could not create an instance of type Screens.Screen. Type is an interace or abstract class and cannot be instantiated. Path 'screen', line 1, position 10.
型
3条答案
按热度按时间6tqwzwtp1#
这不是一个直接的答案,但如果你正在考虑迁移到.NET 7+和
System.Text.Json
,你可以使用System.Text.Json
的能力来处理类型层次结构:字符串
请注意,你的json并不代表屏幕,而是一些屏幕保持器类型,所以:
型
和用法(对于
footer
,我使用了简单类):型
对于Newtonsoft Json.NET,有以下选项:
SerializationBinder
。还请注意,使用
TypeNameHandling
与其他选项以外的None
和没有正确的SerializationBinder
can lead to application vulnerabilities。fiei3ece2#
你在json转换代码中有一个bug,你需要添加一个根类。你还需要在你的json字符串中包含一个类名来指向要使用的类。为了更容易地为所有抽象类创建一个基类
字符串
它将创建一个这样的JSON字符串
型
你可以使用这个转换器从基类转换到具体类
型
irlmq6kh3#
解决方案
如果$type属性被设置为Map到它应该被Map到的确切对象,则仅可能使用TypeNameHandling进行抽象化。如果没有$type属性,则无法使用typenameHandling将其Map到嵌套的抽象对象。
在使用newtonsoft和TypeNameHandling作为设置序列化JSON时,$type被设置。因此,接收到的JSON应该被正确地序列化。
如果这是不可能的,那么有两个“解决方案”:
对我来说,写代码代替转换器更容易更快。我正在用它做原型,但我不推荐它用于生产。
这两种方法都容易在软件包中发生错误,因此不是一个可靠的解决方案,但它是针对特定约束和目标的解决方案。
这是$types的样子:
字符串
在我的例子中,“type”属性对于非结构化来说不是必需的,可以被删除。
在代码中进行初始化
要在代码中实现,你需要在定义屏幕的地方使用一个 Package 器。因为直接实现到屏幕(抽象)是不可能的。
注意
$type应该在type属性的上面,否则它不会被正确处理。我不知道为什么会这样。