← 全部文章 C# 语言

JSON 与 XML 速查

2022 年 6 月 16 日 · 2 分钟读完

目录
  1. JSON
  2. 语法规则
  3. 一个嵌套的例子
  4. XML 的转义字符
  5. XmlDocument 读写时文件被占用
  6. 延伸:C# 里怎么序列化

上位机和 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 里有五个字符要转义:

写法 字符 含义
&lt; < 小于号
&gt; > 大于号
&amp; & 和
&apos; ’ 单引号
&quot; “ 双引号

其中 < 和 & 在文本里必须转义,另外三个在属性值里视情况转义。大段文本(比如一段 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。

# C# # JSON # XML

原文首发于 blog.csdn.net