To learn more about Yaml I encourage you to go to yaml.org, but I'll attempt a short overview.
Yaml uses indentation to denote scope. However, unlike Python, Yaml does not except tabs as indentation. Indentation consists strictly of spaces.
In Yaml, there are 2 types of entities: sequences and mappings. Here is an example of a sequence.
- 1 - apple - orange
A mapping is simply a collection of key to value pairs which is denoted by the key followed by ':' followed by the value. Here is an example of a mapping.
Name: Andre Agassi Birthplace: Las Vegas, Nevada, USA Height: 5'11'' (180 cm) Turned Pro: 1986
Sequences can be inside mappings, and mappings can be inside sequences, the two combined lets you model any imaginable object. Here's an example.
name: John Smith age: 37 spouse: name: Jane Smith age: 25 children: - name: Jimmy Smith age: 15 - name: Jenny Smith age 12
This example says that John Smith has a wife named Jane and two children Jimmy and Jenny, and also gives us their respective ages.
JYaml currently supports the serialzation and deserialization of the following types of Java objects:
JYaml makes it easy to take advantage of the flexibility of Yaml. By default, JYaml converts a sequence to java.util.ArrayList and a mapping to java.util.HashMap. However, this behavior can be overriden by either tagging or type inference, which is what allows for a mapping to be transformed to a JavaBean, or a sequence to be transformed to an array or a different kind of Collection.
To write a Java object to a file in Yaml format, all you have to do is
Yaml.dump(object, new File("object.yml"));
Conversely, to load a Yaml file into Java land
Object object = Yaml.load(new File("object.yml"));
In the examples above, the Yaml file that is output will have tags in them. Tags start with a "!" and is followed by a type identifier. Suppose object was John Smith, the file might look something like the following
--- !Person name: John Smith age: 37 spouse: !Person name: Jane Smith age: 25 children: !Person[] - !Person name: Jimmy Smith age: 15 - !Person name: Jenny Smith age 12
Person must be a JavaBean compliant class that has the JavaBean properties: name, age, spouse, and children. The spouse property has type Person and the children property has type Person[]. Note: a JavaBean must have a null constructor (a constructor with no arguments) as well.
You might be thinking the tags make the file more cluttered and less attractive. I think so too. It is possible to leave out tags by using the minimal output option like so
Yaml.dump(object, new File("object.yml"), true);
This will produce a file that looks like the one in the "Elaborate Example" a couple of sections ago. Now, in order to load the file back into Java land, you'll have to specify the type you're expecting to the JYaml library call
Person person = Yaml.loadType(new File("object.yml"), Person.class);
By providing the top level type in the object hierarchy, JYaml can infer the types of the other objects in the reference graph by using the reflection library. However, this will not work for generic classes such as Collections and Maps. Because of the way generics is designed in Java 5 (using erasure), the type information specified inside the <>'s will be lost during runtime. Thus, for sequences, even with the minimal output option on, eliminating tags will only work with arrays.
Sometimes in a given reference graph, there may be more than one reference to the same object. For example, let's say Jimmy Smith married John's younger sister Jodie Smith, then John would be Jimmys brother-in-law as well as his father. This could be represented in Yaml like this
--- &1 name: John Smith age: 47 spouse: name: Jane Smith age: 35 children: - name: Jimmy Smith age: 25 spouse: &2 name: Jodie Smith age: 30 siblings: - *1 - name: Jenny Smith age 22 siblings: - *2
&1 and &2 are examples of an anchor. Their purpose is to mark an object in the graph with some identifier so that it can be referenced again later by an alias which has the same indentifier (such as *1 and *2). John's id is 1 and Jodie's id is 2 in the example. Anchors and aliases are taken care of by JYaml automatically, but you need to be aware of them when you edit Yaml files.
The "---" you've seen a couple of times already denotes the start of a Yaml document. Thus, you can have more than one Yaml document in the same file or stream. That's right, YamlEncoder may also work with any java.io.OutputStream. To write a stream of objects using JYaml, you need to create a YamlEncoder object, and then invoke its writeObject method each time you need to write a document to the stream. When you are finished it is required to invoke its close method.
YamlEncoder enc = new YamlEncoder(outputStream); enc.writeObject(object1); enc.writeObject(object2); enc.close();
If you have a Collection or simply an Iterator of objects which you want to encode into Yaml, you can use the following shorthand.
Yaml.dumpStream(collection.iterator(), file);
To read from a Yaml stream(this is useful if you have many documents in the stream and don't want to load then all into memory at once), YamlDecoder is used
YamlDecoder dec = new YamlDecoder(inputStream); try{ while (true){ Object object = dec.readObject(); /* do something useful */ } }catch (EOFException e){ System.out.println("Finished reading stream."); }finally dec.close();
Note: anchors and aliases will not work across different documents.
There is also a shorthand for reading multiple documents.
for (Object object: Yaml.loadStream(input)){ /* do something useful */ }
Here, input may be an InputStream, a File, or a String.
A JYaml configuration file can be used to specify default settings for the Yaml encoder and decoder. The configuration file must be named jyaml.yml and may be be placed either in the current directory where the application runs or at the top level of its classpath. A full example of a configuration file is as follows:
minimalOutput: true indentAmount: " " suppressWarnings: true encoding: "ISO-8859-1" transfers: company: com.blah.Company employee: com.blah.Employee
the Yaml formated configuration file is mapped into an object of type YamlConfig. You maybe specify the minimal output, indentation amount, encoding(see here) ,suppress warning options, as well as a way to configure ObjectWrappers (in case you need special handling of you objects that are not JavaBeans). Also, you can set up a mapping here between the transfer names of types you want to use and their fully qualified class names. This is handy if you have a lot of package levels and you want succient Yaml output. In the above example, a tag that read
!com.blah.Company
would now read
!company
Note that the transfer mapping must be a one-to-one mapping.
Java Objects are now constructed and serialied through a construct called the ObjectWrapper. You can make JYaml support custom Java classes by implementing the interfaces CollectionWrapper, MapWrapper, or SimpleObjectWrapper. The best way to learn how to do this is to grab the source and look at what the default implementations of these look like. To try out your ObjectWrapper, you just need to put it in your jyaml.yml, like such:
minimalOutput: true indentAmount: " " suppressWarnings: true encoding: "ISO-8859-1" transfers: company: com.blah.Company employee: com.blah.Employee handlers: com.mycompany.MyFunkyObject: com.mycompany.jyaml.MyFunkyObjectWrapper
More complete documentation of all of the library calls in the JYaml is in the Javadocs.
JYaml has a BSD style open-source license.
Thanks to Rolf Veen for his Yaml parser implementation which jump-started JYaml.
JYaml was written for my own gratification as well as to spread the word about Yaml to the Java community. I welcome suggestions and bug reports to airportyh at users.sourceforge.net.