哈希中数组的YAML缩进

60

我认为在YAML中缩进很重要。

我在irb中测试了以下内容:

> puts({1=>[1,2,3]}.to_yaml)
--- 
1: 
- 1
- 2
- 3
 => nil 

我期望得到这样的结果:
> puts({1=>[1,2,3]}.to_yaml)
--- 
1: 
  - 1
  - 2
  - 3
 => nil 

为什么数组没有缩进?
我在http://www.yaml.org/YAML_for_ruby.html#collections找到了这个问题。
“-”在序列中被视为缩进,因此您可以在映射中添加序列而无需使用空格作为缩进。

显然,在将标量映射到序列时不需要缩进。 - akonsu
4
两者都是有效的。我同意你的观点,它们不应该同时存在。即使官方的YAML网站也有这种情况... https://yaml.org/ - nroose
3个回答

53

简短的回答是两种皆可,因为它们对于YAML解析器来说都是明确无歧义的。其他回答已经指出了这个事实,但请允许我在讨论中添加一些助推剂。

YAML使用缩进不仅仅是为了美观或易读性,当组合不同的数据结构并嵌套它们时,缩进具有关键意义:

# YAML:         # JSON equivalent:
---             # {
one:            #   "one": {
  two:          #     "two": null,
  three:        #     "three": null
                #   }
                # }
                
---             # {
one:            #   "one": {
  two:          #     "two": {
    three:      #       "three": null
                #     }
                #   }
                # }

正如我们所看到的,只需在three之前添加一个缩进级别,就会改变其嵌套级别并删除我们为two分配的以前的null值。

然而,当涉及到列表时,这种行为却不一致,因为它们可以“容忍”我们自然预期会发生的缩进级别降低(如OP所预期),以反映项目的正确嵌套级别。但它仍将以相同的方式工作:

# YAML:         # JSON equivalent:
---             #
one:            #
  two:          #
    - foo       # {            
    - bar       #   "one": {   
                #     "two": [ 
                #       "foo", 
                #       "bar"  
                #     ]        
---             #   }          
one:            # }            
  two:          #
  - foo         #
  - bar         #

以上的第二种形式有些出人意料,打破了缩进级别与嵌套级别相连的想法。明显地,two(一个对象)和嵌套列表都是使用相同的缩进编写的,但却处于不同的嵌套级别。

更糟糕的是,它并不总是有效,而是仅在列表直接放置在对象键下方时才有效。将列表嵌套在其他列表中不会允许自由降低缩进级别,因为这显然会将嵌套元素带到父列表中:

# YAML:         # JSON equivalent:
---             # {
one:            #   "one": {
  two:          #     "two": [
    -           #       null,
    -           #       [
      -         #         null,
      -         #         null
                #       ]
                #     ]
                #   }
                # }
                #
---             # {           
one:            #   "one": {  
  two:          #     "two": [
    -           #       null, 
    -           #       null, 
    -           #       null, 
    -           #       null  
                #     ]       
                #   }         
                # }         

我知道,我知道...别开始说上面的例子有点极端,可能会被认为是边缘情况。它们是完全有效的数据结构,证明了我的观点。当混合对象和嵌套的对象列表时,尤其是它们只有一个键时,更复杂的情况也会发生。这不仅可能导致数据结构声明中的错误,而且变得极难阅读。

以下 YAML 文档是相同的:

# YAML:             # JSON equivalent
---                 # 
one:                # {
  two:              #   "one": {
  - three: foo      #     "two": [
  - bar             #       {"three": "foo"},
  - four:           #       "bar",
    - baz           #       {
    five:           #         "four": ["baz"],
    - fizz          #         "five": ["fizz", "buzz"],
    - buzz          #         "six": null
    six:            #       }
  seven:            #     ],
                    #     "seven": null
---                 #   }
one:                # }
  two:              #      
    - three: foo    # 
    - bar           #
    - four:         #
        - baz       #
      five:         #
        - fizz      #
        - buzz      #
      six:          #
  seven:            #   

我不知道你怎么想,但我认为第二种方式更容易阅读和理解,尤其是在阅读非常大的文档时。第一种方式很容易迷失方向,特别是当对象声明的开头不可见时。缩进级别和嵌套级别之间没有明确的联系。

保持缩进级别与嵌套级别一致 非常重要,可以提高可读性。允许列表中有时省略缩进级别是一件需要非常小心的事情。(翻译:可选的)


5
YAML缩进规则非常反直觉。 - Alan
1
如果您能像为所有其他示例所做的那样,为最后一个示例添加JSON等效内容,那将非常有帮助。但是我同意YAML很糟糕。 - Neutrino

30

就我所知,这两种方式都是有效的:

require 'yaml'

YAML.load(%q{--- 
1:
- 1
- 2
- 3
})
# => {1=>[1, 2, 3]}

YAML.load(%q{--- 
1:
  - 1
  - 2
  - 3
})
# => {1=>[1, 2, 3]}

不清楚您为什么认为连字符前应该有空格。如果您认为这违反了规范,请解释一下原因。

为什么数组没有缩进?

在连字符之前不需要缩进,保持简单即可。


62
虽然不需要空格,但我发现加上空格更易读。 - lfender6445
2
相反,当你有像 Kubernetes 规范这样的对象时,缩进越多,由于额外的空格、滚动和换行,它就越不可读。 - 4c74356b41
12
我敢不同意。在列表的预期缩进水平被压制的情况下,文档变得更加难以阅读,特别是对于像您提到的 k8s 规范这样的大文件。保持缩进与嵌套级别同步非常重要。 - Victor Schröder

13

这是为了让你可以做到:

1: 
- 2: 3
  4: 5
- 6: 7
  8: 9
- 10
=> {1 => [{2 => 3, 4 => 5}, {6 => 7, 8 => 9}, 10]}

基本上,破折号分隔对象,缩进表示键值对的“值”。

那是我所能做到的最好程度;我还没有找到语法背后的任何原因或方面。


13
但是你可以随意这样做...相当于(除第一行外,所有行向右缩进2个空格)得到的结果是相同的。 - Nick
不幸的是,这与我所绑定的Perl 5.18(版本)内置的YAML解析器不兼容。没有缩进,我会得到“YAML错误:映射中的无效元素”。我不确定更新的Perl版本是否已经适应了这个明显合法的语法。 - Myles Prather
更正:如果我在Perl中使用“YAML :: Syck;”,我就能够读取Ruby的默认YAML格式。标准最好的一点是有那么多可以选择的 :)。 - Myles Prather

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