JYaml - Yaml library for
the Java language

Notice: I am no longer maintaining JYaml. See this this Q&A for a list of alternatives.

JYaml Tutorial


Short Introduction to Yaml

To learn more about Yaml I encourage you to go to yaml.org, but I'll attempt a short overview.

Indentation

Yaml uses indentation to denote scope. However, unlike Python, Yaml does not except tabs as indentation. Indentation consists strictly of spaces.

Yaml Entities

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

Elaborate Example

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.


What JYaml can do

JYaml currently supports the serialzation and deserialization of the following types of Java objects:

  • Primitives and respective wrapper classes
  • JavaBean compliant objects (from mappings)
  • Collection (from sequences)
    • List
    • Set
  • Map (from mappings)
  • Arrays (from sequences)
  • BigInteger and BigDecimal
  • Date
  • Custom Java Objects by implementing ObjectsWrappers yourself

JYaml Usage

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.

Minimal Output and Type Inference

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.

Anchors and Aliases

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.

Documents and Streams

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.

Configuration File and Transfer Mapping

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.

ObjectWrappers

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 	
      	

Javadocs

More complete documentation of all of the library calls in the JYaml is in the Javadocs.

License

JYaml has a BSD style open-source license.

Credits

Thanks to Rolf Veen for his Yaml parser implementation which jump-started JYaml.

About

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.