Tuesday, December 8, 2009

Ruby and SnakeYAML

Recently I have seen a post yaml in Java and Ruby where SnakeYAML was not chosen as the YAML parser.
The message is:
Because snakeyaml seems to have a syntactical problem with this YAML document we did no further investigations on snakeyaml.

But the real problem is that the document contains tags and the parser must be instructed how to process them.
Let us see what we can do.
The document:
--- !ruby/object:Test::Module::Object
sub1: !ruby/object:Test::Module::Sub1
att1: []
att2: 0
att3: []
sub2: !ruby/object:Test::Module::Sub2
att1: MyString
att2:
- entry1
att3: 12345
We do not need a lot of code to parse the document:
Constructor con = new Constructor(TestObject.class);
con.addTypeDescription(new TypeDescription(TestObject.class, "!ruby/object:Test::Module::Object"));
con.addTypeDescription(new TypeDescription(Sub1.class, "!ruby/object:Test::Module::Sub1"));
con.addTypeDescription(new TypeDescription(Sub2.class, "!ruby/object:Test::Module::Sub2"));
Yaml yaml = new Yaml(new Loader(con));
return (TestObject) yaml.load(input);
Let's create a YAML document:
DumperOptions options = new DumperOptions();
options.setExplicitStart(true);
options.setExplicitRoot(Tags.MAP);
Yaml yaml2 = new Yaml(options);
String output = yaml2.dump(result);
This is the result:
---
sub1:
att1: []
att2: 0
att3: []
sub2:
att1: MyString
att2: [entry1]
att3: 12345
As you can see no tags are used. Normally SnakeYAML tries to avoid emitting tags as much as possible to simplify inter-language communication. But it seems that dynamically typed languages do not have any other means to define a class of an instance as to use a tag.
Let's emit the tags:
DumperOptions options = new DumperOptions();
options.setExplicitStart(true);
Representer repr = new Representer();
repr.addClassTag(TestObject.class, "!ruby/object:Test::Module::Object");
repr.addClassTag(Sub1.class, "!ruby/object:Test::Module::Sub1");
repr.addClassTag(Sub2.class, "!ruby/object:Test::Module::Sub2");
Yaml yaml2 = new Yaml(new Dumper(repr, options));
String output = yaml2.dump(result);
The document with tags:
--- !ruby/object:Test::Module::Object
sub1: !ruby/object:Test::Module::Sub1
att1: []
att2: 0
att3: []
sub2: !ruby/object:Test::Module::Sub2
att1: MyString
att2: [entry1]
att3: 12345

This document should be properly consumed by the Ruby parser. Unfortunately it requires to use the latest source from Mercurial. Current version (1.5) does not emit the tag for JavaBean properties even when the tag is explicitly defined. Either wait for the release after 1.5 or take the source !
SnakeYAML can work with YAML documents coming from Ruby parser.

Mission Accomplished.

2 comments:

surajz said...

Is there a way to ignore tags for example

Ignore the tags instead of defining each object

--- !ruby/object:ProductPage
originalContent:

Andrey Somov said...

There is no such a thing like "ignore tags". What you wish is in fact to change one tag for another:
'!ruby/object:ProductPage' => '!!com.package.ProductPage' (or !!str, or !!map)

There is a long standing issue for this:
http://code.google.com/p/snakeyaml/issues/detail?id=39

It stays open without any contribution. It also contains a workaround.

There is a way to manage a group of tags which matches a regular expression.

I hope it will help you to find your way.