c# - 反序列化动态 JSON

标签 c# json deserialization

我遇到的问题是如何反序列化以下 JSON。 Answer 的值有时为 NULL、true、整数、 bool 值或包含另一个 JSON 列表(id、描述等)。

我做的第一步是在 Visual Studio 中复制并粘贴 special as class。这为我提供了以下问题类。

然后我尝试反序列化它(遵循 C# - rawResponse 是 JSON)。但是,我得到“Newtonsoft.Json.JsonSerializationException:'无法将当前 JSON 数组(例如 [1,2,3])反序列化为类型 Applicated.Questions,因为该类型需要 JSON 对象(例如 {"name":"value "}) 正确反序列化....“

我知道这与映射不正确有关。

因此,尝试通过将响应保存在动态变量中来将所有这些都放在 foreach 循环中(请注意,我确实为此从 Questions 类中删除了 Property1)。但是,Answer 中的某些结果会将字符串 Id、Description 等存储在 Answer 字段中。有没有更简单的方法,我好像遇到了心理障碍?

JSON:

[
  {
    "Answer": true,
    "QuestionId": 55,
    "Title": "Are you Married?",
    "AnswerType": "Boolean"
  },
  {
    "Answer": {
      "Id": "1",
      "Description": "Female",
      "Reference": "F",
      "ArchiveDate": null,
      "ParentId": null,
      "OptionType": {
        "Id": 40,
        "Type": "dropdown"
      }
    },
    "QuestionId": 778,
    "Title": "Gender",
    "AnswerType": "Option”
  }
]

类:

    public class Questions
    {
        public Class1[] Property1 { get; set; }
    }

    public class Class1
    {
        public object Answer { get; set; }
        public int QuestionId { get; set; }
        public string Title { get; set; }
        public string AnswerType { get; set; }
    }

C#:

Questions result = JsonConvert.DeserializeObject<Questions>(rawResponse);

C# 动态 Foreach

dynamic result = JsonConvert.DeserializeObject(rawResponse);
var lstQuestionObjects = new List<Questions>();

                foreach(var obj in result)
                {
                    lstQuestionObjects.Add(new Questions()
                    {
                        Answer = (obj.Answer !=null) ? obj.Answer.ToString() :"",
                        QuestionId = int.Parse(obj.QuestionId.ToString()),
                        Title = (obj.Title != null) ? obj.Title.ToString() : "",
                        AnswerType = (obj.AnswerType != null) ? obj.AnswerType.ToString() : ""

                    });
                   
                }

最佳答案

这取决于您需要什么级别的互操作性,但首先要认识到的是,在 JSON.Net 中,JSON 文件中的所有 tokens 在内部都继承自 JToken,而不是使用无类型的 < em>object 在您的模型中引用,您将通过将 dynamic 属性键入为 JToken 来获得更好的默认反序列化支持。

这个简单的解决方案在 Question 类上显示了一个帮助方法,该方法将 Answer 解析为强类型对象引用。根据您的实现要求,您甚至可能不需要对答案的强类型访问,但是此方法允许您在反序列化完成后对记录值进行后处理和故障排除。

https://dotnetfiddle.net/FwFyOU

List<Question> questions = JsonConvert.DeserializeObject<List<Question>>(GetJSON());
FiddleHelper.WriteTable(questions);

foreach(var question in questions)
{
    object answer = question.GetTypedAnswer();
    Console.WriteLine("Question: [{0}] {1}: ({2}) {3}", question.QuestionId, question.Title, answer.GetType().Name, answer );
}

模型类定义

注意:根本不需要定义一个名为Questions的类。

public class Question
{
    public JToken Answer { get; set; }
    public int QuestionId { get; set; }
    public string Title { get; set; }
    public string AnswerType { get; set; }  
    
    public object GetTypedAnswer ()
    {
        switch(AnswerType.ToLower())
        {
            case "boolean":
                return Answer.Value<bool>();
            case "string":
                return Answer.Value<string>();
            case "option":
                return (Answer as JObject).ToObject<Option>();
            default:
                return Answer;
        }
    }
}

public class Option
{
    public string Id { get; set; }
    public string Description { get; set; }
    public string Reference { get; set; }
    public DateTime? ArchiveDate { get; set; }
    public string ParentId { get; set; }
    public OptionType OptionType { get;set; }
    
    public override string ToString()
    {
        return String.Format("[{0}] {1} ({2})", Reference, Description, OptionType.Type);
    }
}
public class OptionType
{
    public int Id { get; set; }
    public string Type { get; set; }        
}

输出:

<头>
回答 问题编号 标题 答案类型
是的 55 你结婚了吗? bool 值
{ "Id": "1", "Description": "Female", "Reference": "F", "ArchiveDate": null, "ParentId": null, "OptionType": { "Id": 40 , "类型": "下拉菜单"} } 778 性别 选项
Question: [55] Are you Married?: (Boolean) True
Question: [56] What is your name?: (String) Mr John Doe
Question: [778] Gender: (Option) [F] Female (dropdown)

另一种解决方案是使用 JsonConverter对于 Answer 属性,但是在这种情况下,解析类型所需的元数据是在父 token 中定义的,而不是在值本身中,这意味着您的自定义转换器将需要通过试用来解析类型如果要比较的类型很多,错误可能会非常慢。

折衷方案是为 Question 类本身使用自定义转换器,选择一种方法而不是另一种方法取决于您的需求以及需要解析多少类型(如果需要解析)完全没有。

https://dotnetfiddle.net/m46NaX

    List<Question> questions = JsonConvert.DeserializeObject<List<Question>>(GetJSON());
    FiddleHelper.WriteTable(questions);
    
    foreach(var question in questions)
    {
        Console.WriteLine("Question: [{0}] {1}: ({2}) {3}", question.QuestionId, question.Title, question.Answer.GetType().Name, question.Answer );
    }

Json 转换器实现

[JsonConverter(typeof(QuestionConverter))]
public class Question
{
    public object Answer { get; set; }
    public int QuestionId { get; set; }
    public string Title { get; set; }
    public string AnswerType { get; set; }  
}

public class QuestionConverter : JsonConverter<Question>
{
    public override Question ReadJson(JsonReader reader, Type objectType, Question existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        JObject item = JObject.Load(reader);
        var result = new Question();
        result.QuestionId = item.GetValue(nameof(result.QuestionId)).Value<int>();
        result.Title = item.GetValue(nameof(result.Title)).Value<string>();
        result.AnswerType = item.GetValue(nameof(result.AnswerType)).Value<string>();
        
        var answer = item.GetValue(nameof(result.Answer));
        switch(result.AnswerType.ToLower())
        {
            case "boolean":
                result.Answer = answer.Value<bool>();
                break;
            case "string":
                result.Answer =  answer.Value<string>();
                break;
            case "option":
                result.Answer =  (answer as JObject).ToObject<Option>();
                break;
            default:
                result.Answer =  answer;
                break;
        }

        return result;
    }
    
    public override bool CanWrite{ get => false; }
    public override void WriteJson(JsonWriter writer, Question value, JsonSerializer serializer) {  }
}

public class Option
{
    public string Id { get; set; }
    public string Description { get; set; }
    public string Reference { get; set; }
    public DateTime? ArchiveDate { get; set; }
    public string ParentId { get; set; }
    public OptionType OptionType { get;set; }
    
    public override string ToString()
    {
        return String.Format("[{0}] {1} ({2})", Reference, Description, OptionType.Type);
    }
}
public class OptionType
{
    public int Id { get; set; }
    public string Type { get; set; }        
}

关于c# - 反序列化动态 JSON,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68226154/

相关文章:

c# - WebBrowser 控件 onpropertychange 事件的事件处理程序 - sender 和 e 对象为 null

javascript - Mapper 不包含 CreateMap C# 的定义

python - geoJSON:如何创建具有动态数量特征的FeatureCollection?

android - 使用 SimpleXML 反序列化 XML 时遇到问题

android - 如何反序列化我发送到我的网络服务的数据?

java - Java 二进制数据序列化期间的 InvalidClassException

c# - Stopwatch.ElapsedMilliseconds 和 (stopDateTime - startDateTime).TotalMilliseconds 之间的区别

javascript - 将值从 ViewModel 传递到 JavaScript

java - 使用 jQuery 从 json url 解析变量

javascript - 解析本地存储对象会出现未捕获的语法错误?