目录

snakeyaml官方文档翻译

[TOC]

原文:https://bitbucket.org/snakeyaml/snakeyaml/wiki/Home

安装

Dependency definition (in pom.xml)

1
2
3
4
5
6
7
8
9
<dependencies>
  ...
  <dependency>
    <groupId>org.yaml</groupId>
    <artifactId>snakeyaml</artifactId>
    <version>1.31-SNAPSHOT</version>
  </dependency>
  ...
</dependencies>

使用手册

从实例化org.yaml.snakeyaml.Yaml实例开始

1
Yaml yaml = new Yaml();

加载Yaml

方法 Yaml.load() 将 YAML 文档转换为 Java 对象。

1
2
3
4
5
6
Yaml yaml = new Yaml();
String document = "\n- Hesperiidae\n- Papilionidae\n- Apatelodidae\n- Epiplemidae";
List<String> list = (List<String>) yaml.load(document);
System.out.println(list);

['Hesperiidae', 'Papilionidae', 'Apatelodidae', 'Epiplemidae']

Yaml.load() 接受 String 或 InputStream 对象。Yaml.load(InputStream stream)通过检查流开头的BOM(字节顺序标记)序列来检测编码。如果不存在 BOM,则假定使用 utf-8 编码。

Yaml.load() 返回一个 Java 对象。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public void testLoadFromString() {
    Yaml yaml = new Yaml();
    String document = "hello: 25";
    Map map = (Map) yaml.load(document);
    assertEquals("{hello=25}", map.toString());
    assertEquals(new Long(25), map.get("hello"));
}

public void testLoadFromStream() throws FileNotFoundException {
    InputStream input = new FileInputStream(new File("src/test/resources/reader/utf-8.txt"));
    Yaml yaml = new Yaml();
    Object data = yaml.load(input);
    assertEquals("test", data);
    //
    data = yaml.load(new ByteArrayInputStream("test2".getBytes()));
    assertEquals("test2", data);
}

如果字符串或流包含多个文档,则可以使用 Yaml.loadAll() 方法将它们全部加载。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
---
Time: 2001-11-23 15:01:42 -5
User: ed
Warning:
  This is an error message
  for the log file
---
Time: 2001-11-23 15:02:31 -5
User: ed
Warning:
  A slightly different error
  message.
---
Date: 2001-11-23 15:03:17 -5
User: ed
Fatal:
  Unknown variable "bar"
Stack:
  - file: TopClass.py
    line: 23
    code: |
      x = MoreObject("345\n")
  - file: MoreClass.py
    line: 58
    code: |-
      foo = bar
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public void testLoadManyDocuments() throws FileNotFoundException {
    InputStream input = new FileInputStream(new File(
            "src/test/resources/specification/example2_28.yaml"));
    Yaml yaml = new Yaml();
    int counter = 0;
    for (Object data : yaml.loadAll(input)) {
        System.out.println(data);
        counter++;
    }
    assertEquals(3, counter);
}
1
2
3
4
{Time=Fri Nov 23 21:01:42 CET 2001, User=ed, Warning=This is an error message for the log file}
{Time=Fri Nov 23 21:02:31 CET 2001, User=ed, Warning=A slightly different error message.}
{Date=Fri Nov 23 21:03:17 CET 2001, User=ed, Fatal=Unknown variable "bar", Stack=[{file=TopClass.py, line=23, code=x = MoreObject("345\n")
}, {file=MoreClass.py, line=58, code=foo = bar}]}

仅当调用迭代器时才分析文档(延迟求值)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
     * Parse all YAML documents in the Reader and produce corresponding Java
     * objects. The documents are parsed only when the iterator is invoked.
     *
     * @param yaml YAML data to load from (BOM must not be present)
     * @return an Iterable over the parsed Java objects in this String in proper
     * sequence
     */
    public Iterable<Object> loadAll(Reader yaml) {
        Composer composer = new Composer(new ParserImpl(new StreamReader(yaml),
                loadingConfig.isProcessComments()), resolver, loadingConfig);
        constructor.setComposer(composer);
        Iterator<Object> result = new Iterator<Object>() {
            @Override
            public boolean hasNext() {
                return constructor.checkData();
            }

            @Override
            public Object next() {
                return constructor.getData();
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
        return new YamlIterable(result);
    }

SnakeYAML允许您构造任何类型的Java对象。

1
2
3
4
5
6
none: [~, null]
bool: [true, false, on, off]
int: 42
float: 3.14159
list: [LITE, RES_ACID, SUS_DEXT]
map: {hp: 13, sp: 5}
1
2
3
4
5
6
public void testLoad() throws IOException {
    String doc = Util.getLocalResource("examples/any-object-example.yaml");
    Yaml yaml = new Yaml();
    Map<String, Object> object = (Map<String, Object>) yaml.load(doc);
    System.out.println(object);
}
1
2
{none=[null, null], bool=[true, false, true, false], int=42, float=3.14159, 
list=[LITE, RES_ACID, SUS_DEXT], map={hp=13, sp=5}}

甚至可以构造自定义 Java 类的实例。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
 * 使用对象属性创建实例
 */
public void testGetBeanAssumeClass() {
    String data = "--- !org.yaml.snakeyaml.constructor.Person\nfirstName: Andrey\nage: 99";
    Object obj = construct(data);
    assertNotNull(obj);
    assertTrue("Unexpected: " + obj.getClass().toString(), obj instanceof Person);
    Person person = (Person) obj;
    assertEquals("Andrey", person.getFirstName());
    assertNull(person.getLastName());
    assertEquals(99, person.getAge().intValue());
}

/**
 * 使用构造方法创建实例
 */
public void testGetConstructorBean() {
    String data = "--- !org.yaml.snakeyaml.constructor.Person [ Andrey, Somov, 99 ]";
    Object obj = construct(data);
    assertNotNull(obj);
    assertTrue(obj.getClass().toString(), obj instanceof Person);
    Person person = (Person) obj;
    assertEquals("Andrey", person.getFirstName());
    assertEquals("Somov", person.getLastName());
    assertEquals(99, person.getAge().intValue());
}

/**
 * 使用一个参数的构造方法创建实例
 */
public void testGetConstructorFromScalar() {
    String data = "--- !org.yaml.snakeyaml.constructor.Person 'Somov'";
    Object obj = construct(data);
    assertNotNull(obj);
    assertTrue(obj.getClass().toString(), obj instanceof Person);
    Person person = (Person) obj;
    assertNull("Andrey", person.getFirstName());
    assertEquals("Somov", person.getLastName());
    assertNull(person.getAge());
}

注意:如果要将对象限制为标准 Java 对象(如 List 或 Long),则需要使用 SafeConstructor。

1
Yaml yaml = new Yaml(new SafeConstructor());

提供顶级类型

可以加载没有任何显式标签(是指!开头限定类型)的 YAML 文档。例如,加载此文档(YAML 规范中的示例 2.27)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
invoice: 34843
date   : 2001-01-23
billTo: &id001
    given  : Chris
    family : Dumars
    address:
        lines: |
            458 Walkman Dr.
            Suite #292
        city    : Royal Oak
        state   : MI
        postal  : 48046
shipTo: *id001
product:
    - sku         : BL394D
      quantity    : 4
      description : Basketball
      price       : 450.00
    - sku         : BL4438H
      quantity    : 1
      description : Super Hoop
      price       : 2392.00
tax  : 251.42
total: 4443.52
comments:
    Late afternoon is best.
    Backup contact is Nancy
    Billsmer @ 338-4338.

在Invocie、人员、Address、Product实例中必须提供对象层次结构中的顶级类:

1
Yaml yaml = new Yaml(new Constructor(Invoice.class));

SnakeYAML使用java反射来查找Invoice上所有属性(setters和公共字段)的类。遗憾的是,由于类型擦除,无法在运行时识别类型安全集合的类(泛型类)。<>之间的类信息仅在编译时可用。

隐式类型

当标量节点的tag未显式定义时,SnakeYAML 会尝试对标量节点的内容应用正则表达式来检测实际类型。例如下面几个标量节点:

1
2
3
1.0 -> Float
42 -> Integer
2009-03-30 -> Date

可在通过下面的示例来分析如何更改默认隐式类型解析

examples.resolver.CustomResolverTest

  1. 自定义隐式类型解析器(定义解析用的正则表达式和首字符)

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    public class NoTimeIntResolver extends Resolver {
        public static final Pattern SIMPLE_INT =  Pattern
                .compile("^(?:[-+]?0b[0-1_]+|[-+]?0[0-7_]+|[-+]?(?:0|[1-9][0-9_]*)|[-+]?0x[0-9a-fA-F_]+)$");
       
        /*
         * resolve boolean for only 2 values: true and false
         */
        protected void addImplicitResolvers() {
            addImplicitResolver(Tag.BOOL, BOOL, "tf");
            // define simple int pattern
            addImplicitResolver(Tag.INT, SIMPLE_INT, "-+0123456789");
            addImplicitResolver(Tag.FLOAT, FLOAT, "-+0123456789.");
            addImplicitResolver(Tag.MERGE, MERGE, "<");
            addImplicitResolver(Tag.NULL, NULL, "~nN\0");
            addImplicitResolver(Tag.NULL, EMPTY, null);
            //addImplicitResolver(Tag.TIMESTAMP, TIMESTAMP, "0123456789");
        }
    }
    
  2. 解析标量的代码

    http://img.cana.space/picStore/20211222144159.png

    可以看到就是根据正则表达式确定隐式标量的tag,此处根据resolver解析出2009-01-01的tag是tag:yaml.org,2002:timestamp

泛型集合

当类型安全(泛型)集合为 JavaBean 属性时,SnakeYAML 会动态检测所需的类。从包路径examples.collections可以找到很多例子

如果泛型类型是抽象类(接口),则它不起作用。您必须在 YAML 中放置显式标记或提供显式 TypeDescription。TypeDescription 的目标是收集更多信息并在加载/转储时使用它。

放置显示标记

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
children:
- aaa
- bbb
developers:
- !!examples.collections.TypeSafeListWithInterfaceTest$Developer
  name: Fred
  role: creator
- !!examples.collections.TypeSafeListWithInterfaceTest$Committer
  key: 34
  name: John
  role: committer
name: Bean123

使用TypeDescription

假设我们有下面这个文档

1
2
3
4
5
6
7
plate: 12-XP-F4
wheels:
- {id: 1}
- {id: 2}
- {id: 3}
- {id: 4}
- {id: 5}

我们想要加载这个类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public class Car {
    private String plate;
    private List<Wheel> wheels;

    public String getPlate() {
        return plate;
    }

    public void setPlate(String plate) {
        this.plate = plate;
    }

    public List<Wheel> getWheels() {
        return wheels;
    }

    public void setWheels(List<Wheel> wheels) {
        this.wheels = wheels;
    }
}

其中"车轮"属性是车轮列表。为了加载汽车(并创建List<Wheel>)类型必须提供TypeDescription:

1
2
3
4
5
Constructor constructor = new Constructor(Car.class);//Car.class is root
TypeDescription carDescription = new TypeDescription(Car.class);
carDescription.putListPropertyType("wheels", Wheel.class);
constructor.addTypeDescription(carDescription);
Yaml yaml = new Yaml(constructor);

The full example can be found here (testTypeSafeList()).

类似的方法也适用于Map。请注意,Map的键和值都可以是任何类型:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
plate: 00-FF-Q2
wheels:
  ? {brand: Pirelli, id: 1}
  : 2008-01-16
  ? {brand: Dunkel, id: 2}
  : 2002-12-24
  ? {brand: Pirelli, id: 3}
  : 2008-01-16
  ? {brand: Pirelli, id: 4}
  : 2008-01-16
  ? {brand: Pirelli, id: 5}
  : 2008-01-16

?表示泛型,类比pojo1: !!com.eh.frog.core.test.TestEntity { id: 1 }

The class to be loaded:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public class MyCar {
    private String plate;
    private Map<MyWheel, Date> wheels;

    public String getPlate() {
        return plate;
    }

    public void setPlate(String plate) {
        this.plate = plate;
    }

    public Map<MyWheel, Date> getWheels() {
        return wheels;
    }

    public void setWheels(Map<MyWheel, Date> wheels) {
        this.wheels = wheels;
    }
}

The code:

1
2
3
4
5
6
Constructor constructor = new Constructor(MyCar.class);
TypeDescription carDescription = new TypeDescription(MyCar.class);
carDescription.putMapPropertyType("wheels", MyWheel.class, Object.class);
constructor.addTypeDescription(carDescription);
Yaml yaml = new Yaml(constructor);
MyCar car = (MyCar) yaml.load(<see above>);

转储YAML

The Yaml.dump(Object data) 方法 接受一个Java对象并产生一份Yaml文档. (the source is here)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public void testDump() {
    Map<String, Object> data = new HashMap<String, Object>();
    data.put("name", "Silenthand Olleander");
    data.put("race", "Human");
    data.put("traits", new String[] { "ONE_HAND", "ONE_EYE" });
    Yaml yaml = new Yaml();
    String output = yaml.dump(data);
    System.out.println(output);
}
name: Silenthand Olleander
traits: [ONE_HAND, ONE_EYE]
race: Human

Yaml.dump(Object data, Writer output) 可以将产生的 YAML document 写到指定 file/stream.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public void testDumpWriter() {
    Map<String, Object> data = new HashMap<String, Object>();
    data.put("name", "Silenthand Olleander");
    data.put("race", "Human");
    data.put("traits", new String[] { "ONE_HAND", "ONE_EYE" });
    Yaml yaml = new Yaml();
    StringWriter writer = new StringWriter();
    yaml.dump(data, writer);
    System.out.println(writer.toString());
}

如果需要将多个 YAML 文档转储到单个流,请使用方法 Yaml.dumpAll(Iterator<Object> data)。它接受待序列化到yaml文档中的java 对象迭代器。参数Writer也可以被使用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public void testDumpMany() {
    List<Integer> docs = new LinkedList<Integer>();
    for (int i = 1; i < 4; i++) {
        docs.add(i);
    }
    DumperOptions options = new DumperOptions();
    options.explicitStart(true);
    Yaml yaml = new Yaml(options);
    System.out.println(yaml.dump(docs));
    System.out.println(yaml.dumpAll(docs.iterator()));
}
--- [1, 2, 3]

--- 1
--- 2
--- 3

您甚至可以转储 JavaBeans 的实例。

1
2
3
4
5
6
7
8
public void testDumpCustomJavaClass() {
    Hero hero = new Hero("Galain Ysseleg", -3, 2);
    Yaml yaml = new Yaml();
    String output = yaml.dump(hero);
    System.out.println(output);
    assertEquals("!!examples.Hero {hp: -3, name: Galain Ysseleg, sp: 2}\n", output);
}
!!examples.Hero {hp: -3, name: Galain Ysseleg, sp: 2}

如您所见,JavaBean 中的属性数据是按照字母顺序排列地。(当然可以设置按照属性出现的顺序)

DumperOptions 指定发射器的格式设置详细信息。例如,您可以设置自己喜欢的缩进和宽度,使用规范的 YAML 格式或标量和集合的自定义样式。

自定义样式输出

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public void testDumperOptions() {
    List<Integer> data = new LinkedList<Integer>();
    for (int i = 0; i < 50; i++) {
        data.add(i);
    }
    Yaml yaml = new Yaml();
    String output = yaml.dump(data);
    System.out.println(output);
    //
    DumperOptions options = new DumperOptions();
    options.setWidth(50);
    options.setIndent(4);
    yaml = new Yaml(options);
    output = yaml.dump(data);
    System.out.println(output);
}
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
  23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
  43, 44, 45, 46, 47, 48, 49]

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
    16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
    28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
    40, 41, 42, 43, 44, 45, 46, 47, 48, 49]

规范输出

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public void testDumperOptionsCanonical() {
    List<Integer> data = new LinkedList<Integer>();
    for (int i = 0; i < 5; i++) {
        data.add(i);
    }
    DumperOptions options = new DumperOptions();
    options.setCanonical(true);
    Yaml yaml = new Yaml(options);
    String output = yaml.dump(data);
    System.out.println(output);
}
---
!!seq [
  !!int "0",
  !!int "1",
  !!int "2",
  !!int "3",
  !!int "4",
]
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// Block输出
public void testDumperOptionsFlowStyle() {
    List<Integer> data = new LinkedList<Integer>();
    for (int i = 0; i < 5; i++) {
        data.add(i);
    }
    DumperOptions options = new DumperOptions();
    options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
    Yaml yaml = new Yaml(options);
    String output = yaml.dump(data);
    System.out.println(output);
}
- 0
- 1
- 2
- 3
- 4
  
// 加双引号
public void testDumperOptionsStyle() {
    List<Integer> data = new LinkedList<Integer>();
    for (int i = 0; i < 5; i++) {
        data.add(i);
    }
    DumperOptions options = new DumperOptions();
    options.setDefaultScalarStyle(DumperOptions.ScalarStyle.DOUBLE_QUOTED);
    Yaml yaml = new Yaml(options);
    String output = yaml.dump(data);
    System.out.println(output);
}
- !!int "0"
- !!int "1"
- !!int "2"
- !!int "3"
- !!int "4"

转储自定义Yaml文档

在SnakeYAML中转储的主要目标是生成一个可以加载回实例的YAML文档。在某些情况下,创建的 YAML 文档看起来与预期不符:

  • 不可变对象。不可变对象可能有 getter,但它们没有 setter。默认情况下它作为map 发版,所以之后属性无法被解析。不可变对象必须作为顺序非常重要的序列发版。

  • 注释。到处都有注释可以帮助人类编辑YAML文档

  • 跳过一些 JavaBean 属性(FilterPropertyToDumpTest.java)(空集合、null(SkipBeanTest.java)、计算过的属性等)

  • 定义 JavaBean 属性的顺序(CustomOrderTest.java)。顺序可能对查看信息非常有用:例如,ID或名称通常应该是第一个。(目前,默认情况下按字母顺序排序。

  • 由于标准类型具有多个值,因此可以将表示器配置为使用不同的值。请参阅示例NullTagTest.java和BoolTagTest.java

    • 设置NULL表示样式为空串

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      
      private class NullRepresenter extends Representer {
          public NullRepresenter() {
              super();
              // null representer is exceptional and it is stored as an instance
              // variable.
              this.nullRepresenter = new RepresentNull();
          }
          
          private class RepresentNull implements Represent {
              public Node representData(Object data) {
                  // possible values are here http://yaml.org/type/null.html
                  return representScalar(Tag.NULL, "");
              }
          }
      }
      

由于 YAML 文档只不过是文本文档,因此可以使用任何模板处理器

自定义外观通用流程:

  1. 先尝试是否可以仅使用DumperOptions

  2. 如果不能,请使用 Yaml.dumpAs(obj, Tag.MAP) 来查看 YAML 的样式。

  3. 上面两步还是没有达到你的要求,请拿起你最喜欢的模板引擎,然后继续。

  4. 编写一个将生成的 YAML 文档解析回实例的测试,以便能够检验你的转储功能是ok的。

  5. 您可以随时考虑扩展SnakeYAML代码的可能性。看看 org.yaml.snakeyaml.representer.Representer。如果您发现您的更改有用,请不要忘记将增强功能贡献给SnakeYAML,以便任何人都可以受益。

  6. 实现您自己的方式将实例序列化为文本 YAML 文档

    此处(VelocityTest.java)提供了使用模板的示例。请注意,您可以使用SnakeYAML生成部分结果,然后将这些部分提供给模板进行渲染。

JavaBeans

Yaml规范提到 - “JavaBeans架构的主要目标之一是提供一个平台中立的组件架构。”

避免全局tags可显著提高在不同平台和语言之间交换 YAML 文档的能力。

如果定制 Java 类符合 JavaBean 规范,则可以在没有任何额外代码的情况下加载和转储它。例如,这个JavaBean

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class CarWithWheel {
    private String plate;
    private String year;
    private Wheel wheel;
    private Object part;
    private Map<String, Integer> map;

    public String getPlate() {
        return plate;
    }

    public void setPlate(String plate) {
        this.plate = plate;
    }

    public Wheel getWheel() {
        return wheel;
    }

    public void setWheel(Wheel wheel) {
        this.wheel = wheel;
    }

    public Map<String, Integer> getMap() {
        return map;
    }

    public void setMap(Map<String, Integer> map) {
        this.map = map;
    }

    public Object getPart() {
        return part;
    }

    public void setPart(Object part) {
        this.part = part;
    }

    public String getYear() {
        return year;
    }

    public void setYear(String year) {
        this.year = year;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
CarWithWheel car1 = new CarWithWheel();
car1.setPlate("12-XP-F4");
Wheel wheel = new Wheel();
wheel.setId(2);
car1.setWheel(wheel);
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("id", 3);
car1.setMap(map);
car1.setPart(new Wheel(4));
car1.setYear("2008");
String output = new Yaml().dump(car1);

将会被转储为:

1
2
3
4
5
6
!!package.CarWithWheel
map: {id: 3}
part: !!org.yaml.snakeyaml.constructor.Wheel {id: 4}
plate: 12-XP-F4
wheel: {id: 2}
year: '2008'

请注意,“part"属性仍然具有全局标记,但"wheel"属性没有(因为 wheel 的运行时类(Wheel)与 CarWithWheel 类中定义的(Wheel)相同,而part则不然,运行时类是Wheel,定义的是Object)。

上述JavaBean的示例可以在这里(ImplicitTagsTest.java)找到。转储JavaBean的首选方法是使用Yaml.dumpAs(obj,Tag.MAP)。此实用程序使用块布局发版,并且不会发版具有类名的根全局标记(使用隐式 !!map tag)。

解析 JavaBean 的首选方法是使用 Yaml.loadAs()。它消除了将返回的实例强制转换为指定类的需要。

通常情况下,标准 JavaBean 属性和公共字段是需要输出到文档的。BeanAccess.FIELD使得直接使用私有字段成为可能。

标记快捷方式(ShortCuts)

有一种方法可以为自定义类定义本地标记。

1
2
3
4
5
6
7
8
!!org.yaml.snakeyaml.constructor.Car
plate: 12-XP-F4
wheels:
- !!org.yaml.snakeyaml.constructor.Wheel {id: 1}
- !!org.yaml.snakeyaml.constructor.Wheel {id: 2}
- !!org.yaml.snakeyaml.constructor.Wheel {id: 3}
- !!org.yaml.snakeyaml.constructor.Wheel {id: 4}
- !!org.yaml.snakeyaml.constructor.Wheel {id: 5}

要在转储 Yaml 时消除长名称,应配置使用快捷方式(ClassTagsTest.java):

1
2
3
4
5
Representer representer = new Representer();
representer.addClassTag(Car.class, new Tag("!car"));
representer.addClassTag(Wheel.class, Tag.MAP);;
Yaml yaml = new Yaml(representer, new DumperOptions());
String output = yaml.dump(car);

This is the resulting output:

1
2
3
4
5
6
7
8
!car
plate: 12-XP-F4
wheels:
- {id: 1}
- {id: 2}
- {id: 3}
- {id: 4}
- {id: 5}

加载程序可以采用类似的方式进行配置:

转储使用representer(addClassTag),加载适应Constructor(addTypeDescription)

1
2
3
Constructor constructor = new Constructor();
constructor.addTypeDescription(new TypeDescription(Car.class, "!car"));
Yaml yaml = new Yaml(constructor);

Constructors, representers, resolvers

您可以定义自己的特定于应用程序的标记。(示例的来源在这里(DiceExampleTest))

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class DiceRepresenter extends Representer {
        public DiceRepresenter() {
            this.representers.put(Dice.class, new RepresentDice());
        }

        private class RepresentDice implements Represent {
            public Node representData(Object data) {
                Dice dice = (Dice) data;
                String value = dice.getA() + "d" + dice.getB();
                return representScalar(new Tag("!dice"), value);
            }
        }
    }

例如,您可能希望为以下 Dice 类添加构造器和表示器:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class Dice {
    private Integer a;
    private Integer b;

    public Dice(Integer a, Integer b) {
        super();
        this.a = a;
        this.b = b;
    }

    public Integer getA() {
        return a;
    }

    public Integer getB() {
        return b;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Dice) {
            return toString().equals(obj.toString());
        }
        return false;
    }

    @Override
    public int hashCode() {
        return toString().hashCode();
    }

    @Override
    public String toString() {
        return "Dice " + a + "d" + b;
    }
}

骰子(Dice)对象的默认表示形式并不好:

1
2
3
4
5
6
7
public void testRepresenter() throws IOException {
    Dice dice = new Dice(3, 6);
    Yaml yaml = new Yaml();
    String output = yaml.dump(dice);
    System.out.println(output);
}
!!examples.Dice {a: 3, b: 6}

假设您希望 Dice 对象在 YAML 中表示为 AdB:

1
2
3
System.out.println(yaml.dump(new Dice(3,6)));

3d6

首先,我们定义一个表示器,该表示器将骰子对象转换为带有标记 !dice 的标量节点并注册它。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class DiceRepresenter extends Representer {
    public DiceRepresenter() {
        this.representers.put(Dice.class, new RepresentDice());
    }

    private class RepresentDice implements Represent {
        public Node representData(Object data) {
            Dice dice = (Dice) data;
            String value = dice.getA() + "d" + dice.getB();
            return representScalar(new Tag("!dice"), value);
        }
    }
}

现在,您可以转储 Dice 对象的实例:

1
2
3
4
5
6
7
8
public void testDiceRepresenter() throws IOException {
    Dice dice = new Dice(3, 6);
    Map<String, Dice> data = new HashMap<String, Dice>();
    data.put("gold", dice);
    Yaml yaml = new Yaml(new DiceRepresenter(), new DumperOptions());
    String output = yaml.dump(data);
    System.out.println(output);
}
1
{gold: !dice '10d6'}

让我们添加构造 Dice 对象的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class DiceConstructor extends Constructor {
    public DiceConstructor() {
        this.yamlConstructors.put(new Tag("!dice"), new ConstructDice());
    }

    private class ConstructDice extends AbstractConstruct {
        public Object construct(Node node) {
            String val = (String) constructScalar(node);
            int position = val.indexOf('d');
            Integer a = Integer.parseInt(val.substring(0, position));
            Integer b = Integer.parseInt(val.substring(position + 1));
            return new Dice(a, b);
        }
    }
}

Then you may load a Dice object as well:

1
2
3
4
5
6
public void testConstructor() throws IOException {
    Yaml yaml = new Yaml(new DiceConstructor());
    Object data = yaml.load("{initial hit points: !dice '8d4'}");
    Map<String, Dice> map = (Map<String, Dice>) data;
    assertEquals(new Dice(8, 4), map.get("initial hit points"));
}

您可能希望不要在任何地方都指定标记 !dice。有一种方法可以告诉SankeYAML,任何看起来像XdY的未标记的普通标量都有隐式标签!dice。使用 Yaml.addImplicitResolver(String tag, Pattern regexp, String first),那么你不必指定标签来定义 Dice 对象:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public void testImplicitResolver() throws IOException {
    Yaml yaml = new Yaml(new DiceConstructor(), new DiceRepresenter(), new DumperOptions());
    yaml.addImplicitResolver(new Tag("!dice"), Pattern.compile("\\d+d\\d+"), "123456789");
    // dump
    Map<String, Dice> treasure = (Map<String, Dice>) new HashMap<String, Dice>();
    treasure.put("treasure", new Dice(10, 20));
    String output = yaml.dump(treasure);
    System.out.println(output);
    assertEquals("{treasure: 10d20}\n", output);
    // load
    Object data = yaml.load("{damage: 5d10}");
    Map<String, Dice> map = (Map<String, Dice>) data;
    assertEquals(new Dice(5, 10), map.get("damage"));
}
{treasure: 10d20}

Enum

SnakeYAML以一种特殊的方式对待枚举。(可以在此处找到示例EnumTest.java)

通常,枚举需要显式全局标记:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public void testDumpEnum() {
    Yaml yaml = new Yaml();
    String output = yaml.dump(Suit.CLUBS);
    assertEquals("!!org.yaml.snakeyaml.Suit 'CLUBS'\n", output);
}

public void testLoadEnum() {
    Yaml yaml = new Yaml();
    Suit suit = (Suit) yaml.load("!!org.yaml.snakeyaml.Suit 'CLUBS'");
    assertEquals(Suit.CLUBS, suit);
}

但是,如果 Enum 是 JavaBean 属性(并且该类是隐式定义的(没有感叹号)),则不会使用标记:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public void testDumpEnumBean() {
    DumperOptions options = new DumperOptions();
    options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
    Yaml yaml = new Yaml(options);
    EnumBean bean = new EnumBean();
    bean.setId(17);
    bean.setSuit(Suit.SPADES);
    String output = yaml.dump(bean);
    System.out.println(output);
}
1
2
3
!!org.yaml.snakeyaml.EnumBean
id: 17
suit: SPADES

加载也是一样

1
2
3
4
5
6
public void testLoadEnumBean() {
    Yaml yaml = new Yaml();
    EnumBean bean = (EnumBean) yaml.load("!!org.yaml.snakeyaml.EnumBean\nid: 174\nsuit: CLUBS");
    assertEquals(Suit.CLUBS, bean.getSuit());
    assertEquals(174, bean.getId());
}

线程安全

该实现不是线程安全的。不同的线程可能不会调用同一个实例。线程之间必须具有单独的 Yaml 实例。Yaml 实例的所有内部组件(构造器、表示器)都不能在多线程环境中共享。

在Spring容器中使用

Example of Spring definition: (note: the scope is always ‘prototype’)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd">

    <!-- the most powerful way -->
    <bean id="yamlConstructor" class="examples.CustomConstructor" scope="prototype" />
    <bean id="yamlRepresenter" class="org.yaml.snakeyaml.representer.Representer" scope="prototype" />
    <bean id="yamlOptions" class="org.yaml.snakeyaml.DumperOptions" scope="prototype">
        <property name="indent" value="2" />
    </bean>
    <bean id="snakeYaml" class="org.yaml.snakeyaml.Yaml" scope="prototype">
        <constructor-arg ref="yamlConstructor" />
        <constructor-arg ref="yamlRepresenter" />
        <constructor-arg ref="yamlOptions" />
    </bean>

    <!-- for a single JavaBean -->
    <bean id="beanConstructor" class="org.yaml.snakeyaml.constructor.Constructor" scope="prototype">
        <constructor-arg value="org.yaml.snakeyaml.Invoice" />
    </bean>
    <bean id="javabeanYaml" class="org.yaml.snakeyaml.Yaml" scope="prototype">
        <constructor-arg ref="beanConstructor" />
    </bean>

    <!-- the simplest way -->
    <bean id="standardYaml" class="org.yaml.snakeyaml.Yaml" scope="prototype" />
</beans>

低级接口

可以解析或组合传入的字符流。可以在此处找到用于解析(YamlParseTest.java)的示例,或在此处找到用于组合(YamlComposeTest.java)的示例。

输入必须指定为 java.io.Reader。如果输入是 java.io.InputStream请使用 UnicodeReader.java(以获得适当的 BOM 支持)。

与PyYaml的区别

YAML语法

YAML tags和Java 类型

Collections

Merge

与Yaml规格的偏差