• 【C#】复杂Json的反序列


    前言

    本文用到的Json库是:

     以前用这个

     但是,现在微软主推上面这个,性能更优,用法其实差不多。

    复杂结构的JSON

    如何将一个复杂类型的JSON进行反序列化。那就是如何把json拆解成一个个子类的过程。

    如下我有这样一个json字符串:

    1. {
    2. "success": True,
    3. "message": "操作成功!",
    4. "code": 200,
    5. "result": {
    6. "ruleColumn": [
    7. {
    8. "columnChName": "孔宽1"
    9. },
    10. {
    11. "columnChName": "孔从中心偏移3"
    12. },
    13. {
    14. "columnChName": "孔从中心偏移6"
    15. },
    16. {
    17. "columnChName": "孔从中心偏移5"
    18. },
    19. {
    20. "columnChName": "孔从中心偏移2"
    21. },
    22. {
    23. "columnChName": "孔长3"
    24. },
    25. {
    26. "columnChName": "孔从中心偏移7"
    27. },
    28. {
    29. "columnChName": "孔从中心偏移4"
    30. },
    31. {
    32. "columnChName": "孔中到Y中心8"
    33. },
    34. {
    35. "columnChName": "孔中到X中心9"
    36. }
    37. ],
    38. "number": 5,
    39. "titleColumn": [
    40. {
    41. "columnChName": "工序",
    42. "columnType": "input",
    43. "selectValue": None
    44. },
    45. {
    46. "columnChName": "日期",
    47. "columnType": "date",
    48. "selectValue": None
    49. },
    50. {
    51. "columnChName": "班次",
    52. "columnType": "select",
    53. "selectValue": [
    54. "A",
    55. "B"
    56. ]
    57. },
    58. {
    59. "columnChName": "测试时间",
    60. "columnType": "input",
    61. "selectValue": None
    62. },
    63. {
    64. "columnChName": "测试员",
    65. "columnType": "input",
    66. "selectValue": None
    67. },
    68. {
    69. "columnChName": "测试机台号",
    70. "columnType": "input",
    71. "selectValue": None
    72. },
    73. {
    74. "columnChName": "备注",
    75. "columnType": "input",
    76. "selectValue": None
    77. },
    78. {
    79. "columnChName": "生产机台",
    80. "columnType": "input",
    81. "selectValue": None
    82. },
    83. {
    84. "columnChName": "检验方式",
    85. "columnType": "input",
    86. "selectValue": None
    87. },
    88. {
    89. "columnChName": "判定",
    90. "columnType": "judge",
    91. "selectValue": None
    92. }
    93. ],
    94. "projectDetailId": "1559817674652483586",
    95. "rules": [
    96. {
    97. "columnChName": "孔宽1",
    98. "nominalDim": 17.28,
    99. "tolMax": 0.02,
    100. "tolMin": 0.02,
    101. "usl": 17.3,
    102. "lsl": 17.26
    103. },
    104. {
    105. "columnChName": "孔从中心偏移3",
    106. "nominalDim": 17.28,
    107. "tolMax": 0.02,
    108. "tolMin": 0.02,
    109. "usl": 17.3,
    110. "lsl": 17.26
    111. },
    112. {
    113. "columnChName": "孔从中心偏移6",
    114. "nominalDim": 17.28,
    115. "tolMax": 0.02,
    116. "tolMin": 0.02,
    117. "usl": 17.3,
    118. "lsl": 17.26
    119. },
    120. {
    121. "columnChName": "孔从中心偏移5",
    122. "nominalDim": 13.84,
    123. "tolMax": 0.05,
    124. "tolMin": 0.05,
    125. "usl": 13.89,
    126. "lsl": 13.79
    127. },
    128. {
    129. "columnChName": "孔从中心偏移2",
    130. "nominalDim": 13.84,
    131. "tolMax": 0.05,
    132. "tolMin": 0.05,
    133. "usl": 13.89,
    134. "lsl": 13.79
    135. },
    136. {
    137. "columnChName": "孔长3",
    138. "nominalDim": 52.94,
    139. "tolMax": 0.02,
    140. "tolMin": 0.02,
    141. "usl": 52.96,
    142. "lsl": 52.92
    143. },
    144. {
    145. "columnChName": "孔从中心偏移7",
    146. "nominalDim": 49.75,
    147. "tolMax": 0.02,
    148. "tolMin": 0.02,
    149. "usl": 49.77,
    150. "lsl": 49.73
    151. },
    152. {
    153. "columnChName": "孔从中心偏移4",
    154. "nominalDim": 49.75,
    155. "tolMax": 0.02,
    156. "tolMin": 0.02,
    157. "usl": 49.77,
    158. "lsl": 49.73
    159. },
    160. {
    161. "columnChName": "孔中到Y中心8",
    162. "nominalDim": 43.99,
    163. "tolMax": 0.02,
    164. "tolMin": 0.02,
    165. "usl": 44.01,
    166. "lsl": 43.97
    167. },
    168. {
    169. "columnChName": "孔中到X中心9",
    170. "nominalDim": 16.23,
    171. "tolMax": 0.02,
    172. "tolMin": 0.02,
    173. "usl": 16.25,
    174. "lsl": 16.21
    175. }
    176. ]
    177. },
    178. "timestamp": 1663056576303
    179. }

    其中result这个key对应的内容是可能发生变化的,所以这里可以用到泛型。大体不变的框架是这样的:

    1. {
    2. "success": True,
    3. "message": "添加成功!",
    4. "code": 200,
    5. "result": None,
    6. "timestamp": int(round(time.time() * 1000))
    7. }

    类的构造

    那我先构造这个类,对于大的框架:

    注意,类名并不重要(他代表的是大括号)重要的是属性的名称,需要和key的值保持一致:

    1. public class WeiDaLiResult<T>
    2. {
    3.     public bool success { get; set; }
    4.     public string message { get; set; }
    5.     public int code { get; set; }
    6.     public T result { get; set; }
    7.     public ulong timestamp { get; set; }
    8. }

    然后是针对当前result的部分,这里的技术关键是如何构造json数组,我们用到了IList接口:

    1. #region item 项
    2. public class RuleColumnItem
    3. {
    4. public string columnChName { get; set; }
    5. }
    6. public class TitleColumnItem
    7. {
    8. public string columnChName { get; set; }
    9. public string columnType { get; set; }
    10. public IList<string> selectValue { get; set; }
    11. }
    12. public class RulesItem
    13. {
    14. public string columnChName { get; set; }
    15. public float nominalDim { get; set; }
    16. public float tolMax { get; set; }
    17. public float tolMin { get; set; }
    18. public float usl { get; set; }
    19. public float lsl { get; set; }
    20. }
    21. #endregion
    22. public class SubResult
    23. {
    24. public IList ruleColumn { get; set; }
    25. public int number { get; set; }
    26. public IList titleColumn { get; set; }
    27. public string projectDetailId { get; set; }
    28. public IList rules { get; set; }
    29. }

    反序列化过程调用

    反序列化过程调用(注意泛型是如何被使用的):

    var r = System.Text.Json.JsonSerializer.Deserialize>(str_json);

    中文乱码解决

    这里我们可以把这个options 保存到wpf的App类中,方便其他类调用。

    1. public static JsonSerializerOptions options = new JsonSerializerOptions()
    2. {
    3. // 解决中文乱码问题
    4. Encoder = System.Text.Encodings.Web.JavaScriptEncoder.Create(System.Text.Unicode.UnicodeRanges.All)
    5. };

    比如一个类想序列化成json字符串:

    我们就可以用:

    var byteArray = JsonSerializer.SerializeToUtf8Bytes(this, App.options);

    完整代码如下: 

    1. public class ReturnInfo<T>
    2. {
    3. public bool success { get; set; }
    4. public string message { get; set; }
    5. public string code { get; set; }
    6. public T result { get; set; }
    7. public long timestamp
    8. {
    9. get
    10. {
    11. return DateTimeOffset.Now.ToUnixTimeSeconds();
    12. }
    13. }
    14. public override string ToString()
    15. {
    16. var byteArray = JsonSerializer.SerializeToUtf8Bytes(this, App.options);
    17. string str = System.Text.Encoding.UTF8.GetString(byteArray);
    18. return str;
    19. }
    20. }

    或者obj本身就是一个json,则使用:

    obj.ToJsonString(options)

    套路就是,json对象转换成字符串或者字节数组的时候,都会有重载函数,其中就有一个带options参数的,把这个options传入,就能解决乱码问题啦!

    枚举的反序列化

    这里在做一个优化:

    将columnType这个字段序列化成枚举。

    1. [JsonConverter(typeof(JsonStringEnumConverter))]
    2. public enum ControlType
    3. {
    4. input = 0,
    5. date,
    6. select,
    7. judge,
    8. }

    类改造

    1. //改造前
    2. public class TitleColumnItem
    3. {
    4. public string columnChName { get; set; }
    5. public string columnType { get; set; }
    6. public IList<string> selectValue { get; set; }
    7. }
    8. //改造后
    9. public class TitleColumnItem
    10. {
    11. public string columnChName { get; set; }
    12. public ControlType columnType { get; set; }
    13. public IList<string> selectValue { get; set; }
    14. }

    注意,枚举要序列化,需要加一个特性:

    [JsonConverter(typeof(JsonStringEnumConverter))]

    任意Json获取

    在有的情况下,我们只得到一个json字符串,并没有一个模板来进行序列化,也就是说,这个json的Key是不确定的,那么我们应该这么访问这个json字符串呢?

    我们需要一个叫JsonDocument的类帮忙:

    var je = System.Text.Json.JsonDocument.Parse(jsonStr);

    比如我们的字符串是:

    {"1长 X0":0.01, "12 长 X-26.89":0.07, "判定":"OK"}

    我们就可以这么获取值:

    var s = je.RootElement.GetProperty("1长 X0").GetDouble();

    Json 格式化

    想分行显示json,然后带缩进,本来Newtonsoft.Json是自带这个功能的:

    string json = JsonConvert.SerializeObject(product, Formatting.Indented);

    微软自带的这个包,我还没发现有这个功能,所以,这里就提供一个类来实现吧:

    1. public class JsonHelper
    2. {
    3. private const string INDENT_STRING = " ";
    4. public static string FormatJson(string str)
    5. {
    6. var indent = 0;
    7. var quoted = false;
    8. var sb = new StringBuilder();
    9. for (var i = 0; i < str.Length; i++)
    10. {
    11. var ch = str[i];
    12. switch (ch)
    13. {
    14. case '{':
    15. case '[':
    16. sb.Append(ch);
    17. if (!quoted)
    18. {
    19. sb.AppendLine();
    20. Enumerable.Range(0, ++indent).MyForEach(item => sb.Append(INDENT_STRING));
    21. }
    22. break;
    23. case '}':
    24. case ']':
    25. if (!quoted)
    26. {
    27. sb.AppendLine();
    28. Enumerable.Range(0, --indent).MyForEach(item => sb.Append(INDENT_STRING));
    29. }
    30. sb.Append(ch);
    31. break;
    32. case '"':
    33. sb.Append(ch);
    34. bool escaped = false;
    35. var index = i;
    36. while (index > 0 && str[--index] == '\\')
    37. escaped = !escaped;
    38. if (!escaped)
    39. quoted = !quoted;
    40. break;
    41. case ',':
    42. sb.Append(ch);
    43. if (!quoted)
    44. {
    45. sb.AppendLine();
    46. Enumerable.Range(0, indent).MyForEach(item => sb.Append(INDENT_STRING));
    47. }
    48. break;
    49. case ':':
    50. sb.Append(ch);
    51. if (!quoted)
    52. sb.Append(" ");
    53. break;
    54. default:
    55. sb.Append(ch);
    56. break;
    57. }
    58. }
    59. return sb.ToString();
    60. }
    61. }
    62. static class Extensions
    63. {
    64. public static void MyForEach<T>(this IEnumerable ie, Action action)
    65. {
    66. foreach (var i in ie)
    67. {
    68. action(i);
    69. }
    70. }
    71. }

    2023年4月29日--------------------

    有个小坑大家要注意啦。今天反序列化的时候报了个错误:

    Each parameter in the deserialization constructor on type 'xxxxxx ViewModels.ShowInfo' must bind to an object property or field on deserialization. Each parameter name must match with a property or field on the object. The match can be case-insensitive.

    原因是我的数据类,没有无参的构造函数,因为我自己写了一个有参的构造函数,导致无参的就默认没有了,所以需要手动补上无参的构造函数! 

  • 相关阅读:
    立体仓库货物识别率99.9%!AI让仓储管理事半功倍
    PL2303串口不支持WINDOWS11解决方法
    C#实现OPC DA转OPC UA服务器
    3_使用传统CNN网络训练图像分类模型
    Tetrazine-PEG-SH|Tetrazine-PEG-Thiol|四嗪-聚乙二醇-巯基
    【无标题】
    java毕业设计哈尔滨文旅信息网站(附源码、数据库)
    机器人仓库搬砖
    【GPT‑4o】完整教程:LORA微调LLaMA3并结合RAG和Agent技术实现Text2SQL任务
    001——体验鸿蒙(基于I.MAX6ULL)
  • 原文地址:https://blog.csdn.net/songhuangong123/article/details/126842695