繁体   English   中英

Json.net 反序列化 c# 中的变量类型数组

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM