SnakeYaml-2.0 Proposal Implementation: Separate configuration from input/output Options

24 views
Skip to first unread message

Jordan Angold

unread,
Jun 13, 2011, 3:21:13 PM6/13/11
to SnakeYAML
This post is about the proposed implementation of the change discussed
here: http://groups.google.com/group/snakeyaml-core/browse_thread/thread/a3bb30309cdba4e8
and refers to the branch found here: http://code.google.com/r/jordanangold-separate-config/source/browse

Benefits:
1. Easier to Configure
Instead of extending Constructor or Representer or SafeConstructor or
SafeRepresenter, extend Construct or Represent directly. Add your
custom objects to the configuration classes. For an example, see
DiceExampleTest:
http://code.google.com/r/jordanangold-separate-config/source/browse/src/test/java/examples/DiceExampleTest.java

2. Simpler Code
BaseConstructor, SafeConstructor and Constructor have been merged into
Constructor (the safe/unsafe choice is made in configuration objects).
BaseRepresenter, SafeRepresenter and Representer have also been merged
into Representer. This change makes it easier to track the flow of
control and to debug changes.

3. New Code Patterns
Custom Construct and Represent extensions can be placed anywhere in
the code base, including within the class they construct. This allows
direct access to private constructors, private methods, and so on --
without using reflection. The biggest advantage here is that all code
dealing with class X's implementation can be inside the same file as
class X itself.


Comments and criticism welcome.
/Jordan

maslovalex

unread,
Jun 16, 2011, 7:54:10 AM6/16/11
to snakeya...@googlegroups.com
Hi Jordan.

I did not check the clone yet.

But based on the description I like #1 and #2 (those things has been bothering me from time to time :)

#3 seems also interesting, but to understand it source exploration is needed for me.

I will try to take a look at it next week (when I am on vacation already ;)

-alex

Jordan Angold

unread,
Jun 16, 2011, 11:06:39 AM6/16/11
to SnakeYAML
Hi Alex,

I am glad to hear some interest, and I look forward to your review.

I can provide an example for #3 if you (or anyone else) would like.

/Jordan

Andrey Somov

unread,
Jun 17, 2011, 3:45:38 AM6/17/11
to snakeya...@googlegroups.com
The more information - the better. If we go this way you would need to
provide a comprehensive documentation for those who need to move to
the new API.

-
Andrey

Jordan Angold

unread,
Jun 17, 2011, 4:01:37 PM6/17/11
to SnakeYAML
Andrey: I'll be happy to write a Wiki documentation page, but probably
won't have time for a while.


An example, using the Dice class:

-----
Dice.java:
-----
public class Dice {

// exactly the same as on the Documentation page, except:

public static Construct getConstruct() {
return new ConstructDice();
}

public static Represent getRepresent() {
return new RepresentDice();
}
}

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

class ConstructDice extends AbstractConstruct {
public Object construct(Constructor parent, Node node) {
String val = (String) parent.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);
}
}
-----

The difference is subtle, but important. Now it is easy to create a
Constructor that can construct many kinds of objects with custom
Construct implementations. For example:

Dice: "2d8+6"
Distance: "70 ft." or "500 metres"
Time: "200 seconds" or "3 years"

Before this change, we might have:

DiceConstructor extends Constructor
DistanceConstructor extends DiceConstructor
TimeConstructor extends DistanceConstructor

which I think we can all agree is very messy.

If we knew all the types we wanted to do this for, then we could
create a Constructor that knows about each of them
(EverythingConstructor). However, EverythingConstructor then contains
data about Dice, Distances, Times, and so forth. It would be very easy
for that file to become large and complex, and changing any part of
the Dice, Distance or Time classes would require changing
EverythingConstructor too.

Now we use:

-----
YamlConfig config = YamlConfig.createDefault();

config.getLoaderConfig().getConstructors().putTagConstructor ( new
Tag("!dice"), Dice.getConstruct() );
config.getDumperConfig().getRepresenters().putExactClassRepresenter
( Dice.class, Dice.getRepresent() );

config.getLoaderConfig().getConstructors().putTagConstructor ( new
Tag("!distance"), Distance.getConstruct() );
config.getDumperConfig().getRepresenters().putExactClassRepresenter
( Distance.class, Distance.getRepresent() );

config.getLoaderConfig().getConstructors().putTagConstructor ( new
Tag("!time"), Time.getConstruct() );
config.getDumperConfig().getRepresenters().putExactClassRepresenter
( Time.class, Time.getRepresent() );
-----

This creates a YamlConfig that knows how to use the custom Construct /
Represent instance for each of those types.

-----

If we wanted to make it even easier, we could use:

-----
Dice.java:
-----
public class Dice {
// exactly the same as above, except:

public static void registerConfig ( YamlConfig config ) {
config.getLoaderConfig().getConstructors().putTagConstructor ( new
Tag("!dice"), getConstruct() );
config.getDumperConfig().getRepresenters().putExactClassRepresenter
( Dice.class, getRepresent() );
}
}
-----

Then creating the correct YamlConfig is just:

-----
YamlConfig config = YamlConfig.createDefault();
Dice.registerConfig ( config );
Distance.registerConfig ( config );
Time.registerConfig ( config );
-----

Then, to change the Construct or Represent for Distance, you look in
the Distance.java file.

Most users should not have to touch the Constructor or Representer
classes. They are complex and have a large learning curve. It is still
possible to extend Constructor or Representer (see the
CompactConstructor class, which still works this way), but usually
unnecessary.

/Jordan
Reply all
Reply to author
Forward
0 new messages