如何在Vim中保存时自动格式化JSON

84
说实话,Go语言已经惯坏了我。使用Go时,我习惯了一个由我的编辑器(vim)执行严格格式化规范的环境,这一规范也被团队中几乎所有人和全球其他人所接受和遵循。我希望能在保存JSON文件时以同样的方式进行格式化。

问题:如何在vim中自动格式化/缩进/检查JSON文件?

相关帖子:如何在vim中修复json缩进? - Peter Rincker
11个回答

115

尝试使用一个命令:

execute '%!python -m json.tool' | w  

您可以添加自己的快捷键使其更加简单。当然,为了让这个方法起作用,您需要在您的机器上安装Python。


4
这里的| w是什么目的?为什么不直接写成 :%!python -m json.tool?答:| w 的作用是将当前编辑的文本写入到磁盘中,即保存当前文件。如果你只使用 :%!python -m json.tool 命令,虽然会在 Vim 中将 JSON 进行格式化,但并不会将其写入文件,因此使用 | w 命令可以将格式化后的内容保存到文件中。 - jhrr
4
|w 只是保存缓冲区,如果没有它,你会得到格式化后的 JSON,但更改不会被保存。我想这取决于你想要什么。 - Jose B
啊,我明白了,我之前有点混淆了,我以为它是在将内容传输到bash w 命令中。但是,:write 是有道理的。谢谢! - jhrr
很棒!使用vim默认设置! - Pipo
我一直收到“在第1行发生错误”的错误提示,但是当我在命令行中将同一文件输入到json.tool中时,它可以正常工作。我不明白为什么会这样。 - FilBot3

89
如果您喜欢使用外部工具,并且正在处理json相关工作,我建议使用jq工具:https://stedolan.github.io/jq/。然后在vim中执行:%!jq .命令,它将用jq的输出替换当前缓冲区的内容。

3
如果你想在Windows系统上执行此操作,将单引号改为双引号::%!jq "."。需要先下载Windows版本的jq,并将其从jq-win64.exe(32位版本类似)重命名为jq.exe,同时确保它已被添加到环境变量的路径中。 - Jon V
3
我不需要在“.”周围加引号, :%! jq . 对我有效,或者 :%! jq --indent 4 . - Akavall
2
我更喜欢使用jq而不是python命令,因为Python可能会基于编码更改内容。例如:{"test":"ü"},其中"ü"在执行:%!python - m json.tool后变成了"\u00fc" - Ken Jiiii

26

%!python -m json.tool

或者

%!python -c "import json, sys, collections; print json.dumps(json.load(sys.stdin, object_pairs_hook=collections.OrderedDict), ensure_ascii=False, indent=4)"

你可以把这段添加到你的vimrc文件中:

com! FormatJSON %!python -m json.tool

然后你就可以使用:FormatJson命令格式化json文件了。


1
如果你想禁用 ensure_ascii 或使用自己的 indent,你可以使用 com! FormatJSON %!python -m json.tool --no-ensure-ascii --indent 4。就像 python -m json.tool --help 所建议的那样。 - thetaprime

15

感谢mMontu和Jose B,这就是我最终做的:

警告:这将覆盖您的缓冲区。因此,如果您打开一个已经存在语法错误的json文件,您将丢失整个文件(或可能会丢失文件)。

将此行添加到您的~/.vimrc文件中。

" Ali: to indent json files on save
autocmd FileType json autocmd BufWritePre <buffer> %!python -m json.tool

当然,你需要在你的计算机上安装Python。

编辑:如果你的json出现错误,下面的代码不会覆盖你的缓冲区,这就是正确的答案。但由于我对Vim脚本或者说Shell命令不太熟悉,所以我把它作为一个实验性的东西呈现给你,如果你感觉幸运,可以试试。可能还取决于你的shell环境。请注意。

" Ali: to indent json files on save
autocmd FileType json autocmd BufWritePre <buffer> %!python -m json.tool 2>/dev/null || echo <buffer>

有没有办法更新这个程序,以防止在 JSON 格式不正确的情况下向缓冲区写入。如果能在其中一个辅助窗口中引发错误,那将获得额外加分。 - Josh Peak
使用格式良好的json进行测试会引发错误。我不太了解vim脚本来调试错误,因此我在这里提问。第一个回复对于格式良好的json完美地起作用,而不需要检查清除缓冲区。 - Josh Peak
@NeoZenith,出了什么错误?你使用的是哪个shell?在哪个操作系统上?Vim版本是多少? - Ali
操作系统: OSX 10.10.5, Vim版本:7.4, 错误信息:语法错误,附近出现未预期的标记 ')' (%!python -m json.tool 2>/dev/null || echo <buffer>) - Josh Peak

6
在vim.org上搜索JSON插件返回了以下内容: jdaddy.vim :JSON操作和美化 它有以下说明:

gqaj "pretty prints"(包装/缩进/排序关键字/清理其他)光标下的JSON结构。

如果它能格式化你期望的内容,那么你可以创建一个autocmd BufWritePre在保存时进行格式化。

4
这是我的解决方案。它并没有完全解答“在保存时”这一问题,但如果你在保存之前执行此操作,它将输出错误信息,以便你在保存之前进行修复。
另外,它仅依赖于一个外部工具—— "jq"——它已经成为Unix shell JSON处理工具的黄金标准。而且你可能已经安装了它(仅适用于macOS和Linux/Unix系统;我不知道在Windows下会有什么影响)。
基本上,它只是:
ggVG!jq '.'

这将突出显示整个JSON文档,然后通过 jq 运行它,该工具只会解析正确性,重新格式化(例如,修复任何缩进等),并将输出返回到Vim编辑器。

如果您只想解析文档的一部分,可以手动突出显示该部分,方法是按下vV,然后运行相应命令。

!jq '.'

这里的好处是你可以以这种方式修复文档的子部分。

1
更快 - :%!jq '.' - D. Ben Knoble
@D.BenKnoble 是的,我在之前的回答中看到了那个,但是直到我自己的回答已经写了95%时才看到它。所以我还是提交了我的回答。 - JDS
你甚至不需要加上句点,它是隐含的。 - Silviu

3

2

1

我做了一些整理(尽管其中有些与vim无关),并建议你自己在neovim上编写脚本!

解决方案1:neovim

1-1:自己编写脚本

Neovim允许通过将Python文件或包放置在rplugin/python3/中的runtimepath文件夹中来定义Python3插件。

在我的情况下

- init.vim
- rplugin/python3/[your_py_file_set].py
- rplugin/python3/fmt_file.py

以下是fmt_file.py文件:
# rplugin/python3/fmt_file.py

import pynvim
import json


@pynvim.plugin
class Plugin:
    __slots__ = ('vim',)

    def __init__(self, vim):
        self.vim = vim

    @pynvim.command('FormatJson', nargs='*', range='')
    def format_json(self, args, rg):
        """
        USAGE::

            :FormatJson
        """
        try:
            buf = self.vim.current.buffer
            json_content: str = '\n'.join(buf[:])
            dict_content: dict = json.loads(json_content)
            new_content: str = json.dumps(dict_content, indent=4, sort_keys=True)
            buf[:] = new_content.split('\n')
        except Exception as e:
            self.vim.current.line = str(e)

之后在Nvim中运行:UpdateRemotePlugins一次,以生成必要的Vimscript使您的插件可用。(最好重新启动neovim

然后,打开您想要格式化的JSON文件并输入:FormatJson命令即可完成所有操作。


不要忘记告诉 Vim 你的 Python 的位置

" init.vim

let g:python3_host_prog = '...\python.exe''

并且 pip install pynvim

1-2: 使用 tool.py 工具

tool.py 位于 Lib/json/tool.py 目录下。

:%!python -m json.tool

solution2: 命令行

如果您已经安装了 Python,并且可以打开命令行:

python -m json.tool "test.json" >> "output.json"

解决方案3:Python

我编写了一个简单的脚本来完成这些事情。

"""
USAGE::

    python fmt_file.py fmt-json "C:\test\test.json"
    python fmt_file.py fmt-json "C:\test\test.json"  --out_path="abc.json"
    python fmt_file.py fmt-json "test.json"  --out_path="abc.json"
"""

import click  # pip install click
from click.types import File
import json
from pathlib import Path


@click.group('json')
def gj():
    ...


@gj.command('fmt-json')
@click.argument('file_obj', type=click.File('r', encoding='utf-8'))
@click.option('--out_path', default=None, type=Path, help='output path')
def format_json(file_obj: File, out_path: Path):
    new_content = ''
    with file_obj as f:
        buf_list = [_ for _ in f]
        if buf_list:
            json_content: str = '\n'.join(buf_list)
            dict_content: dict = json.loads(json_content)
            new_content: str = json.dumps(dict_content, indent=4, sort_keys=True)

    if new_content:
        with open(out_path if out_path else Path('./temp.temp_temp.json'),
                  'w', encoding='utf-8') as f:
            f.write(new_content)


def main():
    for register_group in (gj,):
        register_group()


if __name__ == '__main__':
    main()

0
使用ALE在保存时自动格式化 配置ALE以格式化JSON 将以下内容添加到.vim/vimfiles/after/ftplugin/json.vim
let b:ale_fix_on_save = 1 " Fix files when they are saved.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接