JSON 与 XML 速查
2022 年 6 月 16 日 · 2 分钟读完
上位机和 MES、配置文件打交道,绕不开 JSON 和 XML。三篇笔记合在一起。
JSON
JSON(JavaScript Object Notation)是一种轻量的文本数据交换格式。虽然借用了 JavaScript 的语法,但和语言无关,几乎所有语言都有现成的解析库。
{
"sites": [
{ "name": "google", "url": "www.google.com" },
{ "name": "微博", "url": "www.weibo.com" }
]
}
语法规则
- 数据是「名称/值」对,名称必须用双引号;
- 数据之间用逗号分隔,最后一项后面不能有逗号;
- 大括号
{}是对象,中括号[]是数组; - 用反斜杠
\转义字符,比如\"、\\、\n; - 标准 JSON 不支持注释。
对象:无序的名称/值对集合,以 { 开始、} 结束,名称后跟冒号,各对之间用逗号分隔。

数组:值的有序集合,以 [ 开始、] 结束,值之间用逗号分隔。

值:可以是字符串、数字、true、false、null、对象或数组,可以互相嵌套。

一个嵌套的例子
{
"schools": {
"JSschool": [
{ "id": 1.1, "name": "南京小学" },
{ "id": 2, "name": "苏州小学" },
{ "id": 3, "name": "无锡小学" }
],
"JDschool": [
{ "id": 1, "name": "深圳小学", "isOpen": true },
{ "id": 2, "name": "广州小学" }
]
}
}
结构示意:

XML 的转义字符
XML 里有五个字符要转义:
| 写法 | 字符 | 含义 |
|---|---|---|
< |
< | 小于号 |
> |
> | 大于号 |
& |
& | 和 |
' |
’ | 单引号 |
" |
“ | 双引号 |
其中 < 和 & 在文本里必须转义,另外三个在属性值里视情况转义。大段文本(比如一段 SQL 或脚本)可以放进 <![CDATA[ ... ]]>,里面的内容原样保留,不用逐个转义。
用 XmlDocument 或 XDocument 设置节点的值时,转义是自动完成的,只有手动拼 XML 字符串时才需要操心。
XmlDocument 读写时文件被占用
工作中高并发读写 XML 文件时,出现「文件正由另一进程使用」的报错。当时的猜测是 XmlDocument.Load 之后一直占着文件,于是每次用完把对象赋值为 null 并调用 GC.Collect(),问题暂时没再出现。
后来查清楚了:XmlDocument.Load(path) 读完会立刻关闭文件,XmlDocument 本身不持有文件句柄,赋 null 和 GC 并不是解决问题的关键。真正的原因是多个线程同时读写同一个文件:一个线程在 Save 写文件的同时,另一个线程去 Load,就会撞上。GC.Collect() 拖慢了程序,碰巧让冲突变少了。
正确的办法:
private static readonly object _fileLock = new object();
public void SaveConfig(XmlDocument doc, string path)
{
lock (_fileLock) // 同一进程内,读写同一个文件串行化
{
// 先写临时文件再替换,写到一半断电也不会把原文件写坏
string tmp = path + ".tmp";
doc.Save(tmp);
File.Copy(tmp, path, overwrite: true);
File.Delete(tmp);
}
}
public XmlDocument LoadConfig(string path)
{
lock (_fileLock)
{
var doc = new XmlDocument();
doc.Load(path);
return doc;
}
}
如果是多个进程同时访问,可以用 FileStream 配合 FileShare 参数打开,失败时稍等重试;更好的是只让一个程序负责写这个文件。高频读的配置,读一次缓存在内存里,不要每次都去读文件。
延伸:C# 里怎么序列化
- JSON:.NET Core 3.0 起自带
System.Text.Json,JsonSerializer.Serialize(obj)和JsonSerializer.Deserialize<T>(json),性能好、不需要额外装包;.NET Framework 项目或者需要更灵活的功能时用 Newtonsoft.Json(JsonConvert.SerializeObject)。 - System.Text.Json 的两个默认行为容易踩坑:属性名大小写敏感(设置
PropertyNameCaseInsensitive = true),中文会被转义成\uXXXX(设置Encoder = JavaScriptEncoder.Create(UnicodeRanges.All))。 - XML:
XmlSerializer可以把类和 XML 互转;只是读读写写节点的话,LINQ to XML(XDocument、XElement)比XmlDocument写起来简洁得多。 - 配置文件选哪个:新项目更推荐 JSON(
appsettings.json就是),人能读、体积小;和老系统、某些 MES 对接时仍会遇到 XML 和 SOAP,见 WebService。
原文首发于 blog.csdn.net