# XML 格式化 — 排开、压平，或者转成 JSON

排开了好读的 XML，压平了好发的 XML，还有两个方向的 JSON 转换。这些都不会粘进别人的服务器。

> 格式化、缩进和压缩 XML，把 XML 转成 JSON 或者把 JSON 转回 XML。解析器在你的浏览器里跑，什么都不上传，所以一个订阅源、一张发票或者一份配置文件永远不会离开你的设备。

这是一个浏览器交互工具，地址是 https://abox.tools/zh/xml-formatter/。文件在运行该浏览器的设备上处理。主动分享和可选联网功能都在页面上说明。下文是它的文字说明；使用工具请打开该地址。

## 你的 XML 和 JSON **在浏览器里处理**，无须上传处理。

格式化和转换都是对一段字符串做算术，就在这里，在这个页面上。解析器是手写的，在 `src/shared/parse-xml.js` 里，除此之外再没有别的。这个工具压根就没有网络功能，没有要取的，也没有要发的。这一点在这里比“XML”这个词听上去更重要：以这个格式送过来的，多半是一张发票、一份银行流水、一份病历，或者一个头里带着某人凭据的 SOAP 请求。

- ✗ 不上传
- ✗ 不注册
- ✗ 无大小限制
- ✓ 断网可用
- ✓ 开源

## 不上传就把 XML 格式化的步骤

1. **挑一件事做。** 两个标签页，一个输入框：*格式化*把 XML 排开或者压平，*转换*把它变成 JSON，或者把 JSON 变回 XML。你刚排开的那份 XML 就是你要转换的那份，不用粘两遍。
2. **粘进来，或者把文件放上去。** 能选中能复制的都行。把 `.xml`、`.svg`、`.rss` 或 `.xsd` 文件放到选择框上，你自己的浏览器会读它并填进输入框——这里没有上传这一步可以省略。
3. **选缩进，或者压平。** 两个空格、四个空格，或者一个制表符。压平之后还是同一份文档，只是去掉了每一个只为好看而存在的空格，结果里会说这省了多少字节。
4. **在出错的地方读错误。** 在这里失败的解析器会告诉你*哪个标签*没闭合，在第几行第几列，而不是“第 1 行出错”——浏览器一口气读完一份文档之后说的就是那句。点击*跳转到错误位置*就能回到输入中的对应位置。输入时，焦点会留在原处，直到你主动移动它。
5. **把结果拿走。** 复制，或者下载成文件，文件名按它出来的格式取。

## 说得更细的版本

[如何格式化并检查 XML](https://abox.tools/zh/guides/format-and-check-xml/): 在本地格式化、压缩和检查 XML。定位解析错误，在转换为 JSON 前了解属性、文本和数组的映射。

## 工具箱里还有

- [文本比对](https://abox.tools/zh/text-diff/): 两段文本进来，每处差异都被标出来，逐行、逐词。没有一个字被贴进别人的服务器。
- [Base64 编码解码](https://abox.tools/zh/base64/): Base64、百分号编码、HTML 实体、十六进制和反斜杠转义，双向都行。没有一个字被贴进别人的服务器。
- [文本与文件分享](https://abox.tools/zh/share-text/): 分享只活在这个打开的标签页里。读的人从你的浏览器直接取走加密内容，关掉标签页就结束——任何服务器上都不留一点东西。
- [二维码与条形码生成](https://abox.tools/zh/qr-barcode/): 打进去，就变成一个码。生成过程不往外送任何东西。

## 常见问题

### 我的 XML 会被上传到什么地方吗？

不会。这个页面上的解析器和打印器都是在你自己的浏览器里、你自己的硬件上跑的函数。这个工具压根就没有网络功能，从不取任何东西，也从不发任何东西，页面的 `Content-Security-Policy` 里写着它允许联系的每一个地址，没有一个属于本站。这一点对 XML 来说比这个格式的名声听上去更要紧：以它送过来的多半是一张发票、一份银行流水、一份病历，或者一个头里带着凭据的 SOAP 请求。

### 它会解析外部实体吗？

不会，也没有什么可关的。解析外部实体，是把 XML 解析器诓去读运行它的那台机器上的文件的办法，也就是通常写作 XXE 的那个攻击，而 `src/shared/parse-xml.js` 是手写的读取器，里面压根没有实体解析。你的文本也从不交给浏览器自带的 `DOMParser`。`DOCTYPE` 会原样带过去，但永远不会被执行。

### 把 XML 转成 JSON 会丢什么？

混合内容的顺序、注释，还有属性和子元素之间的区别——最后这条是被淡化而不是抹掉，因为属性会变成一个名字以 `@` 开头的成员。元素自己的文本在必须跟别的东西并排时会变成 `#text`，重复的子元素会变成数组。每个值都还是字符串：XML 没有类型，判定 `8080` 是个数字，等于凭空造信息。

### 把 JSON 转成 XML 会丢什么？

空对象、空数组和空字符串之间的区别，三者都会变成一个空元素；还有每个值的类型，因为 XML 没有类型。数组会变成重复的元素，那是唯一能读回来的形状；元素名承载不了的键，会把不方便的字符替换掉，而不是吐出一份没有解析器肯读的文档。

### 它能格式化 SVG、RSS 订阅源或者 POM 文件吗？

能。三者都是 XML，而这里读的是 XML 本身，不是它的某种方言。这样排开的 SVG 更好手改；RSS 或 Atom 订阅源通常是压平了发出来的，不打开就没法读。怎么排版，一点也不改变这份文档的意思。

### 重新缩进会改变 XML 的意思吗？

对于元素里装着元素的文档，不会。可能有影响的是文本：装着词的元素内部的空白就是那段文本的一部分，所以只装文本的元素会留在一行上，而不是被排开。`CDATA` 段会原封不动地抄过去。

### 为什么不直接用浏览器自带的 XML 解析器？

因为文档坏掉时它说的话。`DOMParser` 交回来的是一份错误文档，措辞在每个浏览器里都不一样，而且常常就归结成“第 1 行出错”。手写的读取器能说出哪个标签没闭合、它是在哪儿打开的，而这正是你真正需要知道的。不解析外部实体是另一个理由。

### 文件最大能有多大？

这里没有设限，因为没有服务器在为此付钱。真正的上限是你自己的设备：几兆字节的 XML 没问题，文档很长的时候，页面会等你打字停下来再重新格式化，而不是跟你抢键盘。

### 免费吗？需要注册吗？

免费，没有账号，不用登录，没有试用期，也不限制你粘多少。本站有广告，站点靠它维持；广告拿不到关于你文本的任何东西。

### 断网还能用吗？

能。页面加载一次之后拔掉网线，它照样工作。这也是证明没有上传的最简单办法：一个把你的 XML 送出去格式化的工具，在你拔线的那一刻就停了。

## 这条隐私承诺怎么核实

- **处理在这个浏览器里完成。** 本工具在浏览器里处理输入和结果，处理代码不把它们上传去处理。Content-Security-Policy 限制可用资源和连接目的地，但已允许的主机仍可接收请求。策略不检查载荷，不能单独证明什么都没有发出。请把具体操作的请求与源码核对，或断网完成该本地操作。
- **这里不会去取任何东西。** `src/` 里哪儿都没有 `fetch`，没有 `XMLHttpRequest`，也没有 `sendBeacon`。解析器和打印器都是这个页面里的函数：进去一段字符串，出来一段字符串。
- **外部实体永远不会被解析。** 带外部实体的 `DOCTYPE`，是把 XML 解析器诓去读解析它的那台机器上的文件的办法，也是这个格式最老的一个洞。`src/shared/parse-xml.js` 是手写的读取器，里面压根就没有实体解析这回事——不是关掉了，是根本没有——而且这个页面也从不把你的文本交给浏览器自带的 `DOMParser`。
- **从 XML 出来的每个值都是字符串。** `<port>8080</port>` 并没有说那是不是一个数字，所以 JSON 写的是 `"8080"`。替你决定，就是凭空造出一条信息，然后它还会一路当作文件里本来就有的东西传下去。
- **Google 加载了什么，又没拿到什么。** 广告和统计脚本来自 Google，捐赠按钮来自 Buy Me a Coffee。它们谁也没拿到你文本里的一个字符。读它、解析它、写它的每一行代码，都从这个源提供，也都列在仓库里。
- **断网可用。** 拔掉网线，这个工具一点变化都没有，因为它里面本来就没有联网这一步。这是最简单的证明。

**你可以自己核对。**上面这些不用你信。这个页面是仓库里的模板和配置生成出来的，构建脚本你能读，也能自己跑一遍，跑出来的结果就提交在 `dist` 分支上。拿实际发给你的内容，跟你自己从源码构建出来的结果对一遍就知道了： https://github.com/A-Box-of-Tools/website

最值得先读的几个文件：`config/site.toml` 里是 Content-Security-Policy，告诉你哪个标签没闭合的解析器在 `src/shared/parse-xml.js`，XML 里每个值为什么都按字符串出来、而不是靠猜的，在 `src/convert.js`。
