【问题标题】:Most appropriate file format for storing cross references最适合存储交叉引用的文件格式
【发布时间】:2018-06-01 11:01:37
【问题描述】:

我需要将一些元信息和资产之间的依赖关系存储在文件中,我可以使用这些文件来进行一些验证。

以 JSON 为例,我的元数据文件如下所示(/publish/path/metadata/poster.json):

{
    'created_by': 'John',
    'creation_date': '12112018',
    'version': '005',
    'creator_comments': 'Updated to latest published images for Poppy',
    'path_to_file': '/publish/path/images/poster.png',
    'dependencies': [
                     '/publish/path/metadata/poppy.json',
                     '/publish/path/metadata/dwarf.json',
                     '/publish/path/metadata/giant.json'
                     ]
}

和(/publish/path/metadata/poppy.json):

  {
        'created_by': 'Daug',
        'creation_date': '12102018',
        'version': '003',
        'creator_comments': 'Poppy is more red on top',
        'path_to_file': '/publish/path/images/poppy.png',
        'dependencies': [
                         '/publish/path/metadata/poppy_drawing.json',
                         '/publish/path/metadata/poppy_effect.json'
                         ]
    }

我正在寻找一种最适合执行以下操作的文件格式

  1. 能够存储对其他文件的引用
  2. 可处理引用的 python 库支持
  3. 人类可以轻松阅读
  4. 允许我遍历引用文件的查看器或浏览器支持

你认为什么最适合我的用例?

【问题讨论】:

  • 加载文件时想要什么结果?是否应该加载引用的内容,所以dependencies 最终会成为字典列表?
  • 使用 YAML,您可以发明自己的 !include 标签。在 PyYAML 中,您可以定义加载带有此类标记的节点时要执行的操作。
  • Tinita,我需要将引用解析为字典列表

标签: python json xml reference yaml


【解决方案1】:

JSON、YAML 和 XML 都是流行的文件格式,各有利弊。还有其他文件格式,例如TOMLINI files 等。下面是 JSON、YAML 和 XML 的概述:

JSON

  • 优点
  • 缺点
    • 很难读取多行,因为换行符表示为\n
    • 不太适合手工制作
  • 实施
    • Google 应用凭据
    • NPM package.json
    • Swagger / OpenAPI

YAML

  • 优点
    • 流行且易于阅读的多行值(如果有的话)
    • 规格可用:http://yaml.org/spec/1.2/spec.html
    • 可以参考 Swagger 规范 YAML 实现中所示的外部文件
  • 缺点
    • 出现在核心语言库中的次数不如 JSON 多
  • 一些实现
    • Ruby on Rails 配置文件
    • Swagger / OpenAPI

XML

  • 优点
  • 缺点
    • 非常冗长且难以阅读
    • XML 库用于解析和创建,例如libxml2.
  • 一些实现
    • RSS/原子

总结

  • 如果您的数据量有限并且没有具有多行值的数据,或者不希望人类阅读的多行值,则为 JSON。这对配置文件很有用,因为通常不需要外部依赖项
  • YAML(如果您有更多需要人工创建/编辑的数据,包括多行值)
  • XML 如果你有很多数据,在 GB 范围内

根据您的查看器要求,您可以使用架构来识别文件链接,然后修改现有查看器以在存在时添加链接。当然,您也可以随时从头开始创建自己的。

对于您所说的要求,JSON 和 YAML 似乎是最合适和最流行的。一个好处是有许多通用工具可以来回转换 JSON 和 YAML。自动转换在其他文件格式中并不普遍。

【讨论】:

    【解决方案2】:

    YAML 旨在包含人类可读的数据形式,例如您的数据,(但在 YAML 中有多种表示数据的方式不太可读)

    XML,我已经看到将其描述为具有二进制的可读性和 ASCII 的低效率。

    JSON 的双引号过多,无法突出其实际数据。如果你想手动编辑数据,你真的必须小心数组和对象中的尾随逗号。


    当然没有直接支持您的格式的查看器或浏览器,但如果您从 JSON 开始,则可以编写一个 JavaScript 程序,通过超链接正确显示每个数据集。当您通过浏览器内置的 de DOMparser 开始使用 XML 时,您也可以这样做。 javascript 中也有 YAML 解析器,可以对基于 YAML 的数据执行相同的操作,但必须安装这些解析器并将其加载到浏览器中。


    如果您不想使用 javascript 编程,我会选择将数据放入 YAML 并有一个 Python 程序(递归地)查看所有单独的 YAML 文件并从中生成 HTML,包括正确的超链接(到依赖项的 HTML“版本”)和图像的链接,或图像的适当显示。如果包含 YAML 文档的相应文件具有较新的时间戳,则使程序足够智能以仅(重新)生成 HTML。

    这类似于一些从标记生成静态视图的博客系统的工作方式。而且由于您无论如何都想使用 Python 处理数据,因此您应该能够重用您编写的一些代码。

    你应该让你的/publish/path/metadata/poster.yaml:

    created_by: John
    creation_date: 2018-11-12
    version: 005
    creator_comments: Updated to latest published images for Poppy
    path_to_file: /publish/path/images/poster.png
    dependencies:
    - /publish/path/metadata/poppy.yaml
    - /publish/path/metadata/dwarf.yaml
    - /publish/path/metadata/giant.yaml
    

    正如您所见,您不必将日期写为字符串,YAML 直接支持 YYYY-MM-DD 格式(不清楚您的 creation_date 是美国使用的 MMDDYYYY 还是 DDMMYYYY 更多wideley 用于其他英语国家)。您如何在 HTML 中显示日期当然是您的偏好。

    对于您的 YAML,您应该遵守最新规范(2009 年的 1.2)并使用 ruamel.yaml(免责声明:我是该软件包的作者)。如果你选择 YAML 1.1(在这种情况下你可以使用 PyYAML),你必须引用并将你的版本定义为 PyYAML 的标量字符串,否则将 version: 015 解释为数字 13。ruamel.yaml 也正确地往返和用前导零再次写入此类整数。如果您的版本包含非数字数据,则 YAML 会自动将其加载为字符串(无需引用)。

    对于转储 HTML,有很多选择,使用一些库来创建树形结构,然后转储具有无法生成无效 HTML 的优点。但即使您“手动”生成 HTML,也应该相对快速地调试输出。

    转换程序当然也可以检查所有引用是否存在,如果不存在则警告您。


    执行上述操作的简单程序(输出的 HTML 不太好看):

    from datetime import date
    from pathlib import Path
    from ruamel.yaml import YAML
    from ruamel.yaml.scalarint import ScalarInt
    
    yaml = YAML()
    
    def convert_data(d, fp, level=0):
        """recursively write a loaded YAML document as HTML"""
        if isinstance(d, dict):
            print('<table>', file=fp)
            for k in d:
                print('<tr><td>', file=fp)
                convert_data(k, fp, level=level+1)
                print('</td><td>', file=fp)
                v = d[k]
                convert_data(v, fp, level=level+1)
                print('</td></tr>', file=fp)
            print('</table>', file=fp)
            return
        if isinstance(d, list):
            print('<ul>', file=fp)
            for elem in d:
                print('<li>', file=fp)
                convert_data(elem, fp, level=level+1)
                print('</li>', file=fp)
            print('</ul>', file=fp)
            return
        if isinstance(d, str) and d and d[0] == '/':
            if d.endswith('.yaml'):
                h = Path(d).with_suffix('.html')
                print('<a href="{}">{}</a>'.format(h, d), file=fp)
                return
            if d.endswith('.png'):
                print('<img src="{}">'.format(d), file=fp)
                return
        if isinstance(d, ScalarInt):
            if d._width is not None:
                # integer with leading zeros
                print('{:0{}d}'.format(d, d._width), file=fp)
            return
        if isinstance(d, date):
            # print the date in DDMMYYYY format
            print('{:%d%m%Y}'.format(d), file=fp)
            return
        print(d, file=fp)
    
    def convert_file(yaml_file, html_file):
        data = yaml.load(yaml_file)
        with html_file.open('w') as fp:
            print('<html>\n<body>', file=fp)
            convert_data(data, fp)
            print('</body>\n</html>', file=fp)
    
    def main():
        for yaml_file in Path('.').glob('*.yaml'):
            html_file = yaml_file.with_suffix('.html')
            if True or not html_file.exists() or \
               html_file.stat().st_mtime < yaml_file.stat().st_mtime:
                convert_file(yaml_file, html_file)
    
    
    if __name__ == '__main__':
        main()
    

    您当然可以通过使用标签(!link /publish/path/metadata/poppy.yaml!img /publish/path/images/poster.png 来明确链接和图像,并为这些标签创建带有构造函数的类,然后转储适当的 HTML。但这并不一定会给您提供更好的可读性 YAML。

    【讨论】:

    • 我应该补充一点,在 YAML 1.2 Core Schema 中,003 将作为整数加载,因此从整数和浮点数中去除前导零,因此如果要保留它们,还应该使用引号在 1.2 中
    • @tinita 正如我所指出的,从我添加到答案的程序的输出中可以看出,默认情况下,ruamel.yaml 中的前导零不会被删除。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-15
    • 2016-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多