blob: 93de3702d0209cc6c4841225dd9c765e9c261f90 [file] [log] [blame]
= SnakeYAML Documentation =
''This documentation is very brief and incomplete. Feel free to fix or improve it.''
[[PageOutline]]
== Installation ==
Download the source package and put it to the classpath.
Note that there are some subtle (but not really significant) differences between [http://pyyaml.org/wiki/PyYAML Python] and [http://trac-hg.assembla.com/snakeyaml/wiki SnakeYaml] parsers
and emitters.
== Frequently Asked Questions ==
=== Dictionaries without nested collections are not dumped correctly ===
''Why does''
{{{
Yaml yaml = new Yaml();
String document = " a: 1\n b:\n c: 3\n d: 4\n";
System.out.println(document);
System.out.println(yaml.dump(yaml.load(document)));
}}}
''give''
{{{
a: 1
b:
c: 3
d: 4
a: 1
b: {c: 3, d: 4}
}}}
It's a correct output despite the fact that the style of the nested mapping is different.
By default, SnakeYAML chooses the style of a collection depending on whether it has nested
collections. If a collection has nested collections, it will be assigned the block style.
Otherwise it will have the flow style.
If you want collections to be always serialized in the block style, set the parameter
'''`defaultFlowStyle`''' of '''`DumperOptions`''' to '''`block`'''. For instance,
{{{
DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(DumperOptions.DefaultFlowStyle.BLOCK);
Yaml yaml = new Yaml(options);
String document = " a: 1\n b:\n c: 3\n d: 4\n";
System.out.println(yaml.dump(yaml.load(document)));
a: 1
b:
c: 3
d: 4
}}}
You can find an example [http://trac-hg.assembla.com/snakeyaml/browser/src/test/java/examples/CollectionStyleTest.java here]
=== Binary Data ===
`byte[]` is represented as binary. Also when a `String` contains at least one non-printable character the
`!!binary` type is emitted.
Binary scalar is parsed as `byte[]`.
An example can found [http://trac-hg.assembla.com/snakeyaml/browser/src/test/java/org/yaml/snakeyaml/types/BinaryTagTest.java here].
== Tutorial ==
Start with instantiating the '''`org.yaml.snakeyaml.Yaml`''' instance.
{{{
Yaml yaml = new Yaml();
}}}
=== Loading YAML ===
The method '''`Yaml.load()`''' converts a YAML document to a Java object.
{{{
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']
}}}
You can find an example [http://trac-hg.assembla.com/snakeyaml/browser/src/test/java/examples/LoadExampleTest.java here]
'''`Yaml.load()`''' accepts a String or
an `InputStream` object. '''`Yaml.load(InputStream stream)`''' detects the encoding
by checking the '''BOM''' (byte order mark) sequence at the beginning of the
stream. If no '''BOM''' is present, the '''utf-8''' encoding is assumed.
'''`Yaml.load()`''' returns a Java object.
{{{
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);
}
}}}
If a String or a stream contains several documents, you may load them all with the
'''`Yaml.loadAll()`''' method.
{{{
---
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
}}}
{{{
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);
}
}}}
{{{
{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}]}
}}}
SnakeYAML allows you [http://trac-hg.assembla.com/snakeyaml/browser/src/test/java/examples/AnyObjectExampleTest.java to construct] a Java object of any type.
{{{
none: [~, null]
bool: [true, false, on, off]
int: 42
float: 3.14159
list: [LITE, RES_ACID, SUS_DEXT]
dict: {hp: 13, sp: 5}
}}}
{{{
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);
}
}}}
{{{
{none=[null, null], bool=[true, false, true, false], int=42, float=3.14159,
list=[LITE, RES_ACID, SUS_DEXT], dict={hp=13, sp=5}}
}}}
Even instances of custom Java classes [http://trac-hg.assembla.com/snakeyaml/browser/src/test/java/org/yaml/snakeyaml/constructor/ConstructorTest.java can be constructed].
{{{
/**
* create JavaBean
*/
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());
}
/**
* create instance using constructor arguments
*/
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());
}
/**
* create instance using scalar argument
*/
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());
}
}}}
Note if you want to limit objects to standard Java objects like List or Long you need
[http://trac-hg.assembla.com/snakeyaml/browser/src/test/java/examples/SafeConstructorExampleTest.java to use SafeConstructor].
{{{
Loader loader = new Loader(new SafeConstructor());
Yaml yaml = new Yaml(loader);
}}}
==== Providing the top level type ====
It is possible to load a YAML document without any explicit tags. For instance, to load this document
(example 2.27 from the [http://yaml.org/spec/1.1/#id859060 YAML specification])
{{{
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.
}}}
into Invoice, Person, Address, Product instances the top level class in the object hierarchy
[http://trac-hg.assembla.com/snakeyaml/browser/src/test/java/org/yaml/snakeyaml/Example2_27Test.java must be provided]:
{{{
Loader loader = new Loader(new Constructor(Invoice.class));
Yaml yaml = new Yaml(loader);
}}}
SnakeYAML is using Reflection API to find out the class for all the properties (setters and public fields) on Invoice.
Unfortunately because of erasure it is not possible to identify classes for type safe collections at runtime. The
class information between <> is only available at compile time.
=== Type safe collections ===
In order to parse a collection which contains custom Java classes Yaml has to be provided with additional information.
[http://trac-hg.assembla.com/snakeyaml/browser/src/main/java/org/yaml/snakeyaml/TypeDescription.java TypeDescription]
serves the goal to collect more information and use it while loading/dumping.
Let's say we have this document:
{{{
plate: 12-XP-F4
wheels:
- {id: 1}
- {id: 2}
- {id: 3}
- {id: 4}
- {id: 5}
}}}
and we would like to load this class
{{{
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;
}
}
}}}
where 'wheels' property is a List of Wheel. In order to load Car (and create List<Wheel>)
`TypeDescription` must be provided:
{{{
Constructor constructor = new Constructor(Car.class);//Car.class is root
TypeDescription carDescription = new TypeDescription(Car.class);
carDescription.putListPropertyType("wheels", Wheel.class);
constructor.addTypeDefinition(carDescription);
Loader loader = new Loader(constructor);
Yaml yaml = new Yaml(loader);
}}}
The full example can be found
[http://trac-hg.assembla.com/snakeyaml/browser/src/test/java/org/yaml/snakeyaml/constructor/TypeSafeCollectionsTest.java here]
(testTypeSafeList()).
A similar approach works for Maps. Please note that both keys and values of the Map can be of any type:
{{{
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
}}}
The class to be loaded:
{{{
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 [http://trac-hg.assembla.com/snakeyaml/browser/src/test/java/org/yaml/snakeyaml/constructor/TypeSafeCollectionsTest.java code]:
{{{
Constructor constructor = new Constructor(MyCar.class);
TypeDescription carDescription = new TypeDescription(MyCar.class);
carDescription.putMapPropertyType("wheels", MyWheel.class, Object.class);
constructor.addTypeDefinition(carDescription);
Loader loader = new Loader(constructor);
Yaml yaml = new Yaml(loader);
MyCar car = (MyCar) yaml.load(<see above>);
}}}
=== Dumping YAML ===
The '''`Yaml.dump(Object data)`''' function accepts a Java object and produces a YAML document.
(the source is [http://trac-hg.assembla.com/snakeyaml/browser/src/test/java/examples/DumpExampleTest.java here])
{{{
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)`''' will write the produced YAML document into
the specified file/stream.
{{{
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());
}
}}}
If you need to dump several YAML documents to a single stream, use the method
'''`Yaml.dumpAll(Iterator<Object> data)`'''. It accepts an Iterator of
Java objects to be serialized into a YAML document. A Writer can also be used.
{{{
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
}}}
You may even dump instances of `JavaBeans`.
{{{
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}
}}}
As you can see the `JavaBean` data is sorted althabetically.
'''`DumperOptions`''' specifies
formatting details for the emitter. For instance, you may set the
preferred intendation and width, use the canonical YAML format or
force preferred style for scalars and collections.
{{{
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]
}}}
Canonical output:
{{{
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",
]
}}}
{{{
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.DefaultFlowStyle.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.setDefaultStyle(DumperOptions.DefaultScalarStyle.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"
}}}
=== `JavaBeans` ===
The spec says - "One of the main goals of the `JavaBeans` architecture is to provide a platform neutral component
architecture."
Avoiding global tags significantly improves ability to exchange the YAML documents between different
platforms and languages.
If the custom Java class conforms to the `JavaBean` specification it can be loaded and dumped
without any extra code. For instance this `JavaBean`
{{{
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;
}
}
}}}
{{{
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);
}}}
will be dumped as
{{{
!!package.CarWithWheel
map: {id: 3}
part: !!org.yaml.snakeyaml.constructor.Wheel {id: 4}
plate: 12-XP-F4
wheel: {id: 2}
year: '2008'
}}}
Note that the 'part' property still has a global tag but the 'wheel' property does not
(because the wheel's runtime class is the same as it is defined in the `CarWithWheel` class).
The example for the above `JavaBean` can be found
[http://trac-hg.assembla.com/snakeyaml/browser/src/test/java/org/yaml/snakeyaml/constructor/ImplicitTagsTest.java here]
If it is nessesary to massage/skip the root global tag for the `JavaBean` it
can be specified via `DumperOptions`.setExplicitRoot(String tag).
There is a utility to parse `JavaBeans` - [http://trac-hg.assembla.com/snakeyaml/browser/src/main/java/org/yaml/snakeyaml/JavaBeanParser.java JavaBeanParser].
All the methods of this utility are stateless and can be called from different Threads. The utility eliminates
the need to cast returned instances to the specified class.
=== Shortcuts ===
There is a way to define local tags for custom classes.
{{{
!!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}
}}}
To eliminate long names while dumping Yaml
[http://trac-hg.assembla.com/snakeyaml/browser/src/test/java/org/yaml/snakeyaml/constructor/ClassTagsTest.java should be configured]
to use shortcuts:
{{{
Representer representer = new Representer();
representer.putClassTag(Car.class, "!car");
representer.putClassTag(Wheel.class, "tag:yaml.org,2002:map");
Dumper dumper = new Dumper(representer, new DumperOptions());
Yaml yaml = new Yaml(dumper);
String output = yaml.dump(car);
}}}
This is the resulting output:
{{{
!car
plate: 12-XP-F4
wheels:
- {id: 1}
- {id: 2}
- {id: 3}
- {id: 4}
- {id: 5}
}}}
Loader can be configured in a similar way:
{{{
Constructor constructor = new Constructor();
constructor.putClassTag("!car", Car.class);
Loader loader = new Loader(constructor);
Yaml yaml = new Yaml(loader);
}}}
=== Constructors, representers, resolvers ===
You may define your own application-specific tags. (the example's source is
[http://trac-hg.assembla.com/snakeyaml/browser/src/test/java/examples/DiceExampleTest.java here])
For instance, you may want to add a constructor
and a representer for the following [http://trac-hg.assembla.com/snakeyaml/browser/src/test/java/examples/Dice.java Dice] class:
{{{
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;
}
}
}}}
The default representation for '''`Dice`''' objects is not nice:
{{{
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}
}}}
Suppose you want a '''`Dice`''' object to represented as '''`AdB`''' in YAML:
{{{
System.out.println(yaml.dump(new Dice(3,6)));
3d6
}}}
First we define a representer that convert a dice object to scalar node
with the tag `!dice` and register it.
{{{
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("!dice", value);
}
}
}
}}}
Now you may dump an instance of the `Dice` object:
{{{
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 Dumper(new DiceRepresenter(), new DumperOptions()));
String output = yaml.dump(data);
System.out.println(output);
}
}}}
{{{
{gold: !dice '10d6'}
}}}
Let us add the code to construct a Dice object:
{{{
class DiceConstructor extends Constructor {
public DiceConstructor() {
this.yamlConstructors.put("!dice", new ConstructDice());
}
private class ConstructDice implements Construct {
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:
{{{
public void testConstructor() throws IOException {
Yaml yaml = new Yaml(new Loader(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"));
}
}}}
You might want to not specify the tag `!dice` everywhere. There is a way
to teach SankeYAML that any untagged plain scalar that looks like XdY has
the implicit tag `!dice`. Use '''`Yaml.addImplicitResolver(String tag, Pattern regexp, String first)`'''
then you don't have to specify the tag to define a `Dice` object::
{{{
public void testImplicitResolver() throws IOException {
Yaml yaml = new Yaml(new Loader(new DiceConstructor()), new Dumper(new DiceRepresenter(),
new DumperOptions()));
yaml.addImplicitResolver("!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 treats `Enum`s in a special way. (an example can be found
[http://trac-hg.assembla.com/snakeyaml/browser/src/test/java/org/yaml/snakeyaml/EnumTest.java here])
Normally an `Enum` requires an explicit global tag:
{{{
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);
}
}}}
But if the Enum is a `JavaBean` property (and the class is implicitly defined) then the tags are not used:
{{{
public void testDumpEnumBean() {
DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(DumperOptions.DefaultFlowStyle.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);
}
}}}
{{{
!!org.yaml.snakeyaml.EnumBean
id: 17
suit: SPADES
}}}
The same for loading:
{{{
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());
}
}}}
== Threading ==
Threads must have separate Yaml instances. Instances are cheap both in terms of time to create
and memory to occupy. Only the very first instance is heavy because of static initializers for
constants and regular expressions.
Because Loader and Dumper are stateful they cannot be shared between
different Yaml instances.
== Spring ==
Example of [http://trac-hg.assembla.com/snakeyaml/browser/src/test/resources/examples/spring.xml Spring definition]:
(note: the scope is always 'prototype')
{{{
<?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="yamlLoader" class="org.yaml.snakeyaml.Loader" scope="prototype">
<constructor-arg ref="yamlConstructor" />
</bean>
<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="yamlDumper" class="org.yaml.snakeyaml.Dumper" scope="prototype">
<constructor-arg ref="yamlRepresenter" />
<constructor-arg ref="yamlOptions" />
</bean>
<bean id="snakeYaml" class="org.yaml.snakeyaml.Yaml" scope="prototype">
<constructor-arg ref="yamlLoader" />
<constructor-arg ref="yamlDumper" />
</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="beanLoader" class="org.yaml.snakeyaml.Loader" scope="prototype">
<constructor-arg ref="beanConstructor" />
</bean>
<bean id="javabeanYaml" class="org.yaml.snakeyaml.Yaml" scope="prototype">
<constructor-arg ref="beanLoader" />
</bean>
<!-- the simplest way -->
<bean id="standardYaml" class="org.yaml.snakeyaml.Yaml" scope="prototype" />
</beans>
}}}
== Deviation from PyYAML ==
* SnakeYAML does not emit '...' (end of document) for simple documents
* SnakeYAML emits Unicode (not ASCII) by default
== YAML syntax ==
A good introduction to the YAML syntax is
[http://yaml.org/spec/cvs/current.html#id857168 Chapter 2 of the YAML specification].
You may also check [http://yaml4r.sourceforge.net/cookbook/ the YAML cookbook]. Note
that it is focused on a Ruby implementation and uses the old YAML 1.0 syntax.
Here we present most common YAML constructs together with the corresponding Java objects.
=== Documents ===
YAML stream is a collection of zero or more documents. An empty stream contains no documents.
Documents are separated with '''`---`'''. Documents may optionally end with '''`...`'''.
A single document may or may not be marked with '''`---`'''.
Example of an implicit document:
{{{
- Multimedia
- Internet
- Education
}}}
Example of an explicit document:
{{{
---
- Afterstep
- CTWM
- Oroborus
...
}}}
Example of several documents in the same stream:
{{{
---
- Ada
- APL
- ASP
- Assembly
- Awk
---
- Basic
---
- C
- C# # Note that comments are denoted with ' #' (space and #).
- C++
- Cold Fusion
}}}
=== Block sequences ===
In the block context, sequence entries are denoted by '''`- `''' (dash and space):
{{{
# YAML
- The Dagger 'Narthanc'
- The Dagger 'Nimthanc'
- The Dagger 'Dethanc'
}}}
{{{
# Java
["The Dagger 'Narthanc'", "The Dagger 'Nimthanc'", "The Dagger 'Dethanc'"]
}}}
Block sequences can be nested:
{{{
# YAML
-
- HTML
- LaTeX
- SGML
- VRML
- XML
- YAML
-
- BSD
- GNU Hurd
- Linux
}}}
{{{
# Java
[['HTML', 'LaTeX', 'SGML', 'VRML', 'XML', 'YAML'], ['BSD', 'GNU Hurd', 'Linux']]
}}}
It's not necessary to start a nested sequence with a new line:
{{{
# YAML
- 1.1
- - 2.1
- 2.2
- - - 3.1
- 3.2
- 3.3
}}}
{{{
# Java
[1.1, [2.1, 2.2], [[3.1, 3.2, 3.3]]]
}}}
A block sequence may be nested to a block mapping. Note that in this
case it is not necessary to indent the sequence.
{{{
# YAML
left hand:
- Ring of Teleportation
- Ring of Speed
right hand:
- Ring of Resist Fire
- Ring of Resist Cold
- Ring of Resist Poison
}}}
{{{
# Java
{'right hand': ['Ring of Resist Fire', 'Ring of Resist Cold', 'Ring of Resist Poison'],
'left hand': ['Ring of Teleportation', 'Ring of Speed']}
}}}
=== Block mappings ===
In the block context, keys and values of mappings are separated by '''`: `''' (colon and space):
{{{
# YAML
base armor class: 0
base damage: [4,4]
plus to-hit: 12
plus to-dam: 16
plus to-ac: 0
}}}
{{{
# Java
{'plus to-hit': 12, 'base damage': [4, 4], 'base armor class': 0, 'plus to-ac': 0, 'plus to-dam': 16}
}}}
Complex keys are denoted with '''`? `''' (question mark and space):
{{{
# YAML
? !!python/tuple [0,0]
: The Hero
? !!python/tuple [0,1]
: Treasure
? !!python/tuple [1,0]
: Treasure
? !!python/tuple [1,1]
: The Dragon
}}}
{{{
# Java
{(0, 1): 'Treasure', (1, 0): 'Treasure', (0, 0): 'The Hero', (1, 1): 'The Dragon'}
}}}
Block mapping can be nested:
{{{
# YAML
hero:
hp: 34
sp: 8
level: 4
orc:
hp: 12
sp: 0
level: 2
}}}
{{{
# Java
{'hero': {'hp': 34, 'sp': 8, 'level': 4}, 'orc': {'hp': 12, 'sp': 0, 'level': 2}}
}}}
A block mapping may be nested in a block sequence:
{{{
# YAML
- name: PyYAML
status: 4
license: MIT
language: Python
- name: PySyck
status: 5
license: BSD
language: Python
}}}
{{{
# Java
[{'status': 4, 'language': 'Python', 'name': 'PyYAML', 'license': 'MIT'},
{'status': 5, 'license': 'BSD', 'name': 'PySyck', 'language': 'Python'}]
}}}
=== Flow collections ===
The syntax of flow collections in YAML is very close to the syntax of list and
dictionary constructors in Python:
{{{
# YAML
{ str: [15, 17], con: [16, 16], dex: [17, 18], wis: [16, 16], int: [10, 13], chr: [5, 8] }
}}}
{{{
# Java
{'dex': [17, 18], 'int': [10, 13], 'chr': [5, 8], 'wis': [16, 16], 'str': [15, 17], 'con': [16, 16]}
}}}
=== Scalars ===
There are 5 styles of scalars in YAML: plain, single-quoted, double-quoted, literal, and folded:
{{{
# YAML
plain: Scroll of Remove Curse
single-quoted: 'EASY_KNOW'
double-quoted: "?"
literal: | # Borrowed from http://www.kersbergen.com/flump/religion.html
by hjw ___
__ /.-.\
/ )_____________\\ Y
/_ /=== == === === =\ _\_
( /)=== == === === == Y \
`-------------------( o )
\___/
folded: >
It removes all ordinary curses from all equipped items.
Heavy or permanent curses are unaffected.
}}}
{{{
# Java
{'plain': 'Scroll of Remove Curse',
'literal':
'by hjw ___\n'
' __ /.-.\\\n'
' / )_____________\\\\ Y\n'
' /_ /=== == === === =\\ _\\_\n'
'( /)=== == === === == Y \\\n'
' `-------------------( o )\n'
' \\___/\n',
'single-quoted': 'EASY_KNOW',
'double-quoted': '?',
'folded': 'It removes all ordinary curses from all equipped items. Heavy or permanent curses are unaffected.\n'}
}}}
Each style has its own quirks. A plain scalar does not use indicators to denote its
start and end, therefore it's the most restricted style. Its natural applications are
names of attributes and parameters.
Using single-quoted scalars, you may express any value that does not contain special characters.
No escaping occurs for single quoted scalars except that duplicate quotes '''`''`''' are replaced
with a single quote '''`'`'''.
Double-quoted is the most powerful style and the only style that can express any scalar value.
Double-quoted scalars allow ''escaping''. Using escaping sequences '''`\x**`''' and '''`\u****`''',
you may express any ASCII or Unicode character.
There are two kind of block scalar styles: '''literal''' and '''folded'''. The literal style is
the most suitable style for large block of text such as source code. The folded style is similar
to the literal style, but two consequent non-empty lines are joined to a single line separated
by a space character.
=== Aliases ===
Using YAML you may represent objects of arbitrary graph-like structures. If you want to refer
to the same object from different parts of a document, you need to use anchors and aliases.
Anchors are denoted by the '''`&`''' indicator while aliases are denoted by '''`*`'''. For instance,
the document
{{{
left hand: &A
name: The Bastard Sword of Eowyn
weight: 30
right hand: *A
}}}
expresses the idea of a hero holding a heavy sword in both hands.
SnakeYAML now fully supports recursive objects. For instance, the document
{{{
&A [ *A ]
}}}
will produce a list object containing a reference to itself.
=== Tags ===
Tags are used to denote the type of a YAML node. Standard YAML tags are defined at
http://yaml.org/type/index.html.
Tags may be implicit:
{{{
boolean: true
integer: 3
float: 3.14
}}}
{{{
#!python
{'boolean': True, 'integer': 3, 'float': 3.14}
}}}
or explicit:
{{{
boolean: !!bool "true"
integer: !!int "3"
float: !!float "3.14"
}}}
{{{
#!python
{'boolean': True, 'integer': 3, 'float': 3.14}
}}}
Plain scalars without explicitly defined tag are subject to implicit tag
resolution. The scalar value is checked against a set of regular expressions
and if one of them matches, the corresponding tag is assigned to the scalar.
SnakeYAML allows an application to add custom implicit tag resolvers.
== YAML tags and Java types ==
The following table describes how nodes with different tags are converted
to Java objects.
|| '''YAML tag''' || '''Java type''' ||
|| ''Standard YAML tags'' || ||
|| `!!null` || `null` ||
|| `!!bool` || `Boolean` ||
|| `!!int` || `Integer, Long, BigInteger` ||
|| `!!float` || `Double` ||
|| `!!binary` || `String` ||
|| `!!timestamp` || `java.util.Date`, `java.sql.Date` ||
|| `!!omap`, `!!pairs` || `List` of `Object[]` ||
|| `!!set` || `Set` ||
|| `!!str` || `String` ||
|| `!!seq` || `List` ||
|| `!!map` || `Map` ||
An example of loading and dumping [http://yaml.org/spec/1.1/#id858961 Global tags] can be found
[http://trac-hg.assembla.com/snakeyaml/browser/src/test/java/org/yaml/snakeyaml/Example2_24Test.java here].
== Collections ==
Default implementations of collections are:
* ''List'': `LinkedList`
* ''Map'': `LinkedHashMap` (the order is implicitly defined)
It is possible to define other default implementations. An example can be found
[http://trac-hg.assembla.com/snakeyaml/browser/src/test/java/examples/CustomListExampleTest.java here] for List and
[http://trac-hg.assembla.com/snakeyaml/browser/src/test/java/examples/CustomMapExampleTest.java here] for Map
== Deviations from the specification ==
''need to update this section''
* rules for tabs in YAML are confusing. We are close, but not there yet.
Perhaps both the spec and the parser should be fixed. Anyway, the best
rule for tabs in YAML is to not use them at all.
* Byte order mark. The initial BOM is stripped, but BOMs inside the stream
are considered as parts of the content. It can be fixed, but it's not
really important now.
* ~~Empty plain scalars are not allowed if alias or tag is specified.~~ This
is done to prevent anomalities like '''[ !tag, value]''', which can be
interpreted both as '''[ !<!tag,> value ]''' and '''[ !<!tag> "", "value" ]'''.
The spec should be fixed.
* Indentation of flow collections. The spec requires them to be indented
more than their block parent node. Unfortunately this rule renders many intuitively
correct constructs invalid, for instance,
{{{
block: {
} # this is indentation violation according to the spec.
}}}
* ':' is not allowed for plain scalars in the flow mode. ~~~'''{1:2}''' is
interpreted as '''{ 1 : 2 }'''.~~~