[英]Json.net deserialize array of varible types in c#
我有一个第三方 API 正在返回一个 JSON object 具有不同类型的数组。 例如,一个数组 json object 看起来像这样:
[{
"text": "<p>Introduction</p>",
"id": 13273,
"item_type": "Message",
"page_id": 5292,
"position": 1,
"alias": null,
"html_class": null,
"include_condition": null
}, {
"text": "<p><span style=\"background-color: transparent; color: rgb(0, 0, 0);\">Value Proposition</span></p>",
"id": 13274,
"item_type": "Message",
"page_id": 5292,
"position": 2,
"alias": null,
"html_class": null,
"include_condition": null
}, {
"start_value": 0,
"end_value": 10,
"start_text": "Not At All Common Need",
"mid_text": null,
"end_text": "Extremely Common Need",
"enable_not_applicable": false,
"not_applicable_text": null,
"choices": [{
"id": 43181,
"text": "0",
"alias": null,
"position": 1,
"is_default": false,
"is_other": false,
"is_none_of_above": false,
"points": 0.0000,
"image": null
}, {
"id": 43182,
"text": "1",
"alias": null,
"position": 2,
"is_default": false,
"is_other": false,
"is_none_of_above": false,
"points": 1.0000,
"image": null
}, {
"id": 43183,
"text": "2",
"alias": null,
"position": 3,
"is_default": false,
"is_other": false,
"is_none_of_above": false,
"points": 2.0000,
"image": null
}, {
"id": 43184,
"text": "3",
"alias": null,
"position": 4,
"is_default": false,
"is_other": false,
"is_none_of_above": false,
"points": 3.0000,
"image": null
}, {
"id": 43185,
"text": "4",
"alias": null,
"position": 5,
"is_default": false,
"is_other": false,
"is_none_of_above": false,
"points": 4.0000,
"image": null
}, {
"id": 43186,
"text": "5",
"alias": null,
"position": 6,
"is_default": false,
"is_other": false,
"is_none_of_above": false,
"points": 5.0000,
"image": null
}, {
"id": 43187,
"text": "6",
"alias": null,
"position": 7,
"is_default": false,
"is_other": false,
"is_none_of_above": false,
"points": 6.0000,
"image": null
}, {
"id": 43188,
"text": "7",
"alias": null,
"position": 8,
"is_default": false,
"is_other": false,
"is_none_of_above": false,
"points": 7.0000,
"image": null
}, {
"id": 43189,
"text": "8",
"alias": null,
"position": 9,
"is_default": false,
"is_other": false,
"is_none_of_above": false,
"points": 8.0000,
"image": null
}, {
"id": 43190,
"text": "9",
"alias": null,
"position": 10,
"is_default": false,
"is_other": false,
"is_none_of_above": false,
"points": 9.0000,
"image": null
}, {
"id": 43191,
"text": "10",
"alias": null,
"position": 11,
"is_default": false,
"is_other": false,
"is_none_of_above": false,
"points": 10.0000,
"image": null
}],
"layout": "Horizontal",
"show_separator": false,
"option_width": null,
"question_text": "<p><span style=\"background-color: transparent; color: rgb(0, 0, 0);\">To what degree is the need described common in your research/work? (choose only one)</span></p>",
"subtext": "",
"is_required": true,
"item_position": "Left",
"question_text_position": "Top",
"id": 13275,
"item_type": "RadioButtonScale",
"page_id": 5292,
"position": 3,
"alias": "",
"html_class": null,
"include_condition": null
}, {
"script": "\n$(function() {\n localStorage.setItem('Speeder', new Date());\n $('.btn-next').hide();\n $('.btn-prev').hide();\n\n function BIO_showButton() {\n $('.btn-next').show();\n $('.btn-prev').show();\n }\n setTimeout(BIO_showButton, 5000);\n namespace.checkForSurveyComplete();\n});",
"id": 13276,
"item_type": "Javascript",
"page_id": 5292,
"position": 4,
"alias": null,
"html_class": null,
"include_condition": null
}]
另一个数组如下所示:
{{
"rows": [
{
"id": 1123,
"position": 1,
"row_type": "Normal",
"text": "First Row",
"alias": null,
"include_condition": {
"expressions": [],
"groups": [],
"logical_operator": "OR"
}
},
{
"id": 1124,
"position": 2,
"row_type": "Normal",
"text": "Second Row",
"alias": null,
"include_condition": {
"expressions": [],
"groups": [],
"logical_operator": "OR"
}
},
{
"id": 1125,
"position": 3,
"row_type": "Normal",
"text": "Thrid Row",
"alias": null,
"include_condition": {
"expressions": [],
"groups": [],
"logical_operator": "OR"
}
},
{
"id": 1126,
"position": 4,
"row_type": "Normal",
"text": "Fourth Row",
"alias": null,
"include_condition": {
"expressions": [],
"groups": [],
"logical_operator": "OR"
}
}
],
"columns": [
{
"id": 1079,
"position": 1,
"column_type": "RowTexts",
"prototype_item": null,
"require_unique_answers": false,
"width": 0
},
{
"id": 1080,
"position": 2,
"column_type": "Question",
"prototype_item": {
"layout": "Vertical",
"columns": 1,
"show_number_labels": false,
"choices": [
{
"id": 43713,
"text": "1st Radio Grid",
"alias": null,
"position": 0,
"is_default": false,
"is_other": false,
"is_none_of_above": false,
"points": null,
"image": null
},
{
"id": 43714,
"text": "2nd Radio Grid",
"alias": null,
"position": 1,
"is_default": false,
"is_other": false,
"is_none_of_above": false,
"points": null,
"image": null
},
{
"id": 43715,
"text": "3rd Radio Grid",
"alias": null,
"position": 2,
"is_default": false,
"is_other": false,
"is_none_of_above": false,
"points": null,
"image": null
},
{
"id": 43716,
"text": "4th Radio Grid",
"alias": null,
"position": 3,
"is_default": false,
"is_other": false,
"is_none_of_above": false,
"points": null,
"image": null
},
{
"id": 43717,
"text": "Last Radio Grid",
"alias": null,
"position": 4,
"is_default": false,
"is_other": false,
"is_none_of_above": false,
"points": null,
"image": null
}
],
"randomize": false,
"allow_other": false,
"question_text": "<p><span style=\"font-family: Lato;\">Select one of each</span></p>",
"subtext": "",
"is_required": false,
"item_position": "Left",
"question_text_position": "Top",
"id": 13516,
"item_type": "RadioButtons",
"alias": null,
"html_class": null,
"include_condition": null
},
"require_unique_answers": false,
"width": 0
}
],
"elements": [
{
"row": 1,
"column": 1,
"item": {
"text": "First Row",
"id": 13517,
"item_type": "Message",
"alias": null,
"html_class": null,
"include_condition": null
}
},
{
"row": 1,
"column": 2,
"item": {
"prototype_item_type": "RadioButtons",
"prototype_item_id": 13516,
"id": 13521,
"item_type": "MatrixQuestionColumnElement",
"alias": null,
"html_class": null,
"include_condition": null
}
},
{
"row": 2,
"column": 1,
"item": {
"text": "Second Row",
"id": 13518,
"item_type": "Message",
"alias": null,
"html_class": null,
"include_condition": null
}
},
{
"row": 2,
"column": 2,
"item": {
"prototype_item_type": "RadioButtons",
"prototype_item_id": 13516,
"id": 13522,
"item_type": "MatrixQuestionColumnElement",
"alias": null,
"html_class": null,
"include_condition": null
}
},
{
"row": 3,
"column": 1,
"item": {
"text": "Thrid Row",
"id": 13519,
"item_type": "Message",
"alias": null,
"html_class": null,
"include_condition": null
}
},
{
"row": 3,
"column": 2,
"item": {
"prototype_item_type": "RadioButtons",
"prototype_item_id": 13516,
"id": 13523,
"item_type": "MatrixQuestionColumnElement",
"alias": null,
"html_class": null,
"include_condition": null
}
},
{
"row": 4,
"column": 1,
"item": {
"text": "Fourth Row",
"id": 13520,
"item_type": "Message",
"alias": null,
"html_class": null,
"include_condition": null
}
},
{
"row": 4,
"column": 2,
"item": {
"prototype_item_type": "RadioButtons",
"prototype_item_id": 13516,
"id": 13524,
"item_type": "MatrixQuestionColumnElement",
"alias": null,
"html_class": null,
"include_condition": null
}
}
],
"grid_lines": "None",
"row_text_align": "Left",
"width": null,
"question_text": "<p><span style=\"font-family: Lato;\">This is the Grid Radio Question</span></p>",
"subtext": "",
"is_required": false,
"item_position": "Left",
"question_text_position": "Top",
"id": 13515,
"item_type": "Matrix",
"page_id": 5326,
"position": 1,
"alias": null,
"html_class": null,
"include_condition": null
}}
请注意它是如何在每个数组元素中具有完全不同的值的两项数组。 因此,如果我有一个与数组中的每个对象匹配的 c# object,我如何让 JSON 将每个数组元素反序列化为各自的类型? (有一个包含 item_type 和 text 的基本类型)。 我有这个工作:
object[] objValues = JsonConvert.DeserializeObject<object[]>(strJson, settings);
baseObject[] objBaseValues = JsonConvert.DeserializeObject<baseObject[]>(strJson, settings);
然后,我正在查看 objBaseValues 以找出哪种类型,然后从 json object 中重新转换每个数组元素,如下所示:
List<baseObject> objReturn = new List<baseObject>();
for (int i = 0; i < objBaseValues.Length; i++)
{
if (objBaseValues[i].item_type.ToLower() == "matrix")
{
objReturn.Add(JsonConvert.DeserailizeObject<MatrixPageItem>(JsonConver.SerializeObject(objValues[i]), settings);
}
else
{
objReturn.Add(JsonConvert.DeserializeObject<PageItem>(JsonConvert.SerializeObject(objValues[i]), settings);
}
}
这是有效的,但有更好的方法吗? TIA,
好吧。 这可能会很长 xD 所以请耐心等待。
我为解决这个问题所做的是创建一个实现 JsonConverter 的 object。 我对这个自定义实现所做的就是在 object 中抓取一些东西,以确定它是什么类型。 然后激活匹配的类型并调用 serializer.Populate(reader, instance) 将球传回默认的 jsonconverter。
这是我在生产中的一些代码:我在评论中写了一些说明
public class JsonTypeIdBasedConverter : JsonConverter
{
public override bool CanWrite => false;
public override bool CanRead => true;
//I do not know what this needs to be for you. Because what happens is, the JsonConverter walks over the type specified in the generic parameter.
//It walks over its propertyTypes and passes that type on to this method to decide if this object was meant to parse for the type.
//So what I would suggest is you make a wrapper object representing the list, that has a list of items of type interface for the two or more objects that you want to parse to and decide here of the property is a list of that specific interface
//If you simply do return true, that will not work because then this object will also get called for every other type of parsing, you do not want that :)
public override bool CanConvert(Type objectType)
=> typeof(ResolvableByTypeId).IsAssignableFrom(objectType);
public override void WriteJson(JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
=> throw new InvalidOperationException("Use default serialization.");
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
{
var jsonToken = JToken.Load(reader);
if (jsonToken.Type == JTokenType.Null)
return null;
//this for me was the identifying attribute in the json object that revealed what type is was for them, so I could grab our
string typeId = jsonToken["TypeId"].Value<string>();
object result = CreateObjectByTypeId(typeId);
serializer.Populate(jsonToken.CreateReader(), result);
return result;
}
private object CreateObjectByTypeId(string typeId)
{
... grab type from somewhere, list? switch?
return Activator.CreateInstance(typeToCreate);
}
}
Serializer.Populate 然后进一步遍历新实例化的 object 的所有属性,并递归地执行它通常所做的事情。
然后,该接口需要有一个属性来标记它需要使用此转换器类型进行解析:
namespace SomeNamespace
{
[JsonConverter(typeof(JsonTypeIdBasedConverter))]
public interface Condition
{
bool Validate(Context context);
}
}
我已经三次检查了我的代码......我认为这就是它的全部内容。 但是,如果我弄错了,请回复评论,如果这对您有帮助,我会再次跳过所有内容::)
-- PS:
我不认为代码中的长屁股注释是相当透明的......所以我会试着阐明我的意思。
I have a class that has a list of type interface (1), since json does not know what concrete type it actually needs to resolve to, I label the interface with a tagging interface(2) and make a custom json converter(3)识别接口(4),但确实知道它需要根据 json 文件中的信息解析的类型。
(1)
public class Wrapper
{
List<AbstractItemType> _unknownType
}
(3)[JsonConverter(typeof(AbstractItemJsonConverter))]
(2)public Interface AbstractItemType : TaggingInterface {}
public Interface TaggingInterface {}
public class ConcreteItem1 : AbstractItemType
{
}
public class ConcreteItem2 : AbstractItemType
{
}
(3)
public class AbstractItemJsonConverter : JsonConverter
{
...
(4)
public override bool CanConvert(Type objectType)
=> typeof(TaggingInterface).IsAssignableFrom(objectType);
...
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.