本文用到的Json库是:

以前用这个
但是,现在微软主推上面这个,性能更优,用法其实差不多。
如何将一个复杂类型的JSON进行反序列化。那就是如何把json拆解成一个个子类的过程。
如下我有这样一个json字符串:
- {
- "success": True,
- "message": "操作成功!",
- "code": 200,
- "result": {
- "ruleColumn": [
- {
- "columnChName": "孔宽1"
- },
- {
- "columnChName": "孔从中心偏移3"
- },
- {
- "columnChName": "孔从中心偏移6"
- },
- {
- "columnChName": "孔从中心偏移5"
- },
- {
- "columnChName": "孔从中心偏移2"
- },
- {
- "columnChName": "孔长3"
- },
- {
- "columnChName": "孔从中心偏移7"
- },
- {
- "columnChName": "孔从中心偏移4"
- },
- {
- "columnChName": "孔中到Y中心8"
- },
- {
- "columnChName": "孔中到X中心9"
- }
- ],
- "number": 5,
- "titleColumn": [
- {
- "columnChName": "工序",
- "columnType": "input",
- "selectValue": None
- },
- {
- "columnChName": "日期",
- "columnType": "date",
- "selectValue": None
- },
- {
- "columnChName": "班次",
- "columnType": "select",
- "selectValue": [
- "A",
- "B"
- ]
- },
- {
- "columnChName": "测试时间",
- "columnType": "input",
- "selectValue": None
- },
- {
- "columnChName": "测试员",
- "columnType": "input",
- "selectValue": None
- },
- {
- "columnChName": "测试机台号",
- "columnType": "input",
- "selectValue": None
- },
- {
- "columnChName": "备注",
- "columnType": "input",
- "selectValue": None
- },
- {
- "columnChName": "生产机台",
- "columnType": "input",
- "selectValue": None
- },
- {
- "columnChName": "检验方式",
- "columnType": "input",
- "selectValue": None
- },
- {
- "columnChName": "判定",
- "columnType": "judge",
- "selectValue": None
- }
- ],
- "projectDetailId": "1559817674652483586",
- "rules": [
- {
- "columnChName": "孔宽1",
- "nominalDim": 17.28,
- "tolMax": 0.02,
- "tolMin": 0.02,
- "usl": 17.3,
- "lsl": 17.26
- },
- {
- "columnChName": "孔从中心偏移3",
- "nominalDim": 17.28,
- "tolMax": 0.02,
- "tolMin": 0.02,
- "usl": 17.3,
- "lsl": 17.26
- },
- {
- "columnChName": "孔从中心偏移6",
- "nominalDim": 17.28,
- "tolMax": 0.02,
- "tolMin": 0.02,
- "usl": 17.3,
- "lsl": 17.26
- },
- {
- "columnChName": "孔从中心偏移5",
- "nominalDim": 13.84,
- "tolMax": 0.05,
- "tolMin": 0.05,
- "usl": 13.89,
- "lsl": 13.79
- },
- {
- "columnChName": "孔从中心偏移2",
- "nominalDim": 13.84,
- "tolMax": 0.05,
- "tolMin": 0.05,
- "usl": 13.89,
- "lsl": 13.79
- },
- {
- "columnChName": "孔长3",
- "nominalDim": 52.94,
- "tolMax": 0.02,
- "tolMin": 0.02,
- "usl": 52.96,
- "lsl": 52.92
- },
- {
- "columnChName": "孔从中心偏移7",
- "nominalDim": 49.75,
- "tolMax": 0.02,
- "tolMin": 0.02,
- "usl": 49.77,
- "lsl": 49.73
- },
- {
- "columnChName": "孔从中心偏移4",
- "nominalDim": 49.75,
- "tolMax": 0.02,
- "tolMin": 0.02,
- "usl": 49.77,
- "lsl": 49.73
- },
- {
- "columnChName": "孔中到Y中心8",
- "nominalDim": 43.99,
- "tolMax": 0.02,
- "tolMin": 0.02,
- "usl": 44.01,
- "lsl": 43.97
- },
- {
- "columnChName": "孔中到X中心9",
- "nominalDim": 16.23,
- "tolMax": 0.02,
- "tolMin": 0.02,
- "usl": 16.25,
- "lsl": 16.21
- }
- ]
- },
- "timestamp": 1663056576303
- }
其中result这个key对应的内容是可能发生变化的,所以这里可以用到泛型。大体不变的框架是这样的:
- {
- "success": True,
- "message": "添加成功!",
- "code": 200,
- "result": None,
- "timestamp": int(round(time.time() * 1000))
- }
那我先构造这个类,对于大的框架:
注意,类名并不重要(他代表的是大括号)重要的是属性的名称,需要和key的值保持一致:
- public class WeiDaLiResult<T>
- {
- public bool success { get; set; }
- public string message { get; set; }
- public int code { get; set; }
- public T result { get; set; }
- public ulong timestamp { get; set; }
- }
然后是针对当前result的部分,这里的技术关键是如何构造json数组,我们用到了IList接口:
- #region item 项
- public class RuleColumnItem
- {
- public string columnChName { get; set; }
- }
-
- public class TitleColumnItem
- {
- public string columnChName { get; set; }
- public string columnType { get; set; }
- public IList<string> selectValue { get; set; }
- }
- public class RulesItem
- {
- public string columnChName { get; set; }
- public float nominalDim { get; set; }
- public float tolMax { get; set; }
- public float tolMin { get; set; }
- public float usl { get; set; }
- public float lsl { get; set; }
- }
- #endregion
-
- public class SubResult
- {
- public IList
ruleColumn { get; set; } - public int number { get; set; }
- public IList
titleColumn { get; set; } - public string projectDetailId { get; set; }
- public IList
rules { get; set; } -
- }
反序列化过程调用(注意泛型是如何被使用的):
var r = System.Text.Json.JsonSerializer.Deserialize>(str_json);
这里我们可以把这个options 保存到wpf的App类中,方便其他类调用。
- public static JsonSerializerOptions options = new JsonSerializerOptions()
- {
- // 解决中文乱码问题
- Encoder = System.Text.Encodings.Web.JavaScriptEncoder.Create(System.Text.Unicode.UnicodeRanges.All)
- };
比如一个类想序列化成json字符串:
我们就可以用:
var byteArray = JsonSerializer.SerializeToUtf8Bytes(this, App.options);
完整代码如下:
- public class ReturnInfo<T>
- {
-
-
- public bool success { get; set; }
- public string message { get; set; }
- public string code { get; set; }
- public T result { get; set; }
- public long timestamp
- {
- get
- {
- return DateTimeOffset.Now.ToUnixTimeSeconds();
- }
- }
-
-
- public override string ToString()
- {
- var byteArray = JsonSerializer.SerializeToUtf8Bytes(this, App.options);
- string str = System.Text.Encoding.UTF8.GetString(byteArray);
- return str;
- }
- }
或者obj本身就是一个json,则使用:
obj.ToJsonString(options)
套路就是,json对象转换成字符串或者字节数组的时候,都会有重载函数,其中就有一个带options参数的,把这个options传入,就能解决乱码问题啦!
这里在做一个优化:
将columnType这个字段序列化成枚举。
- [JsonConverter(typeof(JsonStringEnumConverter))]
-
- public enum ControlType
- {
- input = 0,
- date,
- select,
- judge,
- }
- //改造前
- public class TitleColumnItem
- {
- public string columnChName { get; set; }
- public string columnType { get; set; }
- public IList<string> selectValue { get; set; }
- }
-
-
- //改造后
- public class TitleColumnItem
- {
- public string columnChName { get; set; }
- public ControlType columnType { get; set; }
- public IList<string> selectValue { get; set; }
- }
注意,枚举要序列化,需要加一个特性:
[JsonConverter(typeof(JsonStringEnumConverter))]
在有的情况下,我们只得到一个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,然后带缩进,本来Newtonsoft.Json是自带这个功能的:
string json = JsonConvert.SerializeObject(product, Formatting.Indented);
微软自带的这个包,我还没发现有这个功能,所以,这里就提供一个类来实现吧:
- public class JsonHelper
- {
- private const string INDENT_STRING = " ";
- public static string FormatJson(string str)
- {
- var indent = 0;
- var quoted = false;
- var sb = new StringBuilder();
- for (var i = 0; i < str.Length; i++)
- {
- var ch = str[i];
- switch (ch)
- {
- case '{':
- case '[':
- sb.Append(ch);
- if (!quoted)
- {
- sb.AppendLine();
- Enumerable.Range(0, ++indent).MyForEach(item => sb.Append(INDENT_STRING));
- }
- break;
- case '}':
- case ']':
- if (!quoted)
- {
- sb.AppendLine();
- Enumerable.Range(0, --indent).MyForEach(item => sb.Append(INDENT_STRING));
- }
- sb.Append(ch);
- break;
- case '"':
- sb.Append(ch);
- bool escaped = false;
- var index = i;
- while (index > 0 && str[--index] == '\\')
- escaped = !escaped;
- if (!escaped)
- quoted = !quoted;
- break;
- case ',':
- sb.Append(ch);
- if (!quoted)
- {
- sb.AppendLine();
- Enumerable.Range(0, indent).MyForEach(item => sb.Append(INDENT_STRING));
- }
- break;
- case ':':
- sb.Append(ch);
- if (!quoted)
- sb.Append(" ");
- break;
- default:
- sb.Append(ch);
- break;
- }
- }
- return sb.ToString();
- }
-
-
- }
-
- static class Extensions
- {
- public static void MyForEach<T>(this IEnumerable
ie, Action action ) - {
- foreach (var i in ie)
- {
- action(i);
- }
- }
- }
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.
原因是我的数据类,没有无参的构造函数,因为我自己写了一个有参的构造函数,导致无参的就默认没有了,所以需要手动补上无参的构造函数!
