Friday, 20 August 2010

Joda-Convert

Type conversion to and from a String is a very common application problem. To aid in the process, I've created a small library - Joda-Convert.

Joda-Convert

There are many libraries designed for converting from one object to another, so why create another. Well, partly because I wanted something focussed on the task of Object to String rather than the larger problem of Object to Object. See this list (bottom of the page) for other projects.

The second reason I wanted to write Joda-Convert was to experiment with annotation based conversion. This is an extension of the principles in JSR-311 (RESTful API) where type conversion is performed by looking for a valueOf static method or a constructor that takes a String. While this approach is very useful, it doesn't support some of my projects like JSR-310, where the static method is named of or parse.

This leads into the third reason. If the annotation based approach is successful, then Joda-Convert is ideally placed to be moved into the JDK. It would be wonderful to see the JDK classes annotated to indicate which methods should be used to convert to and from String.

Using Joda-Convert is easy:

// conversion to String
String str = StringConvert.INSTANCE.convertToString(foo);

// conversion from String
Foo bar = StringConvert.INSTANCE.convertFromString(Foo.class, str);

There are built in converters for many JDK types. It is also possible to create an instance of StringConvert to allow application converters to be registered.

When searching for a converter, if one is not already registered, then the system will search the class for annotations:

public class Distance {

  @FromString
  public static Distance parse(String str) { ... }

  @ToString
  public String getStandardOutput() { ... }

}

As can be seen, adding the annotations to a class is very simple. The Joda-Convert system will parse the class, find the annotations and create a suitable converter that calls the annotated methods by reflection. The key point is that they can be used on any method, rather than the convention approach used by JSR-311. Consider a class like TimeZone to understand why this is important.

One little known point about annotations is that the annotation class is not needed at runtime. This means that a library like Commons-Lang could add the annotations to their Mutable* classes without actually adding a runtime dependency. If a user chose to not use Joda-Convert then there would be no impact. If a user did choose to use Joda-Convert, then the conversion would just work.

If I can release Joda-Convert soon, then I will add the annotations to Joda-Time and Joda-Money. Hopefully other projects will consider the idea too! (If you're interested, let me know, as it gives an incentive to get the project to v1.0)

Summary

Joda-Convert is currently an untested Alpha that exists to test out the annotation idea (although as far as I know it does work). I'd welcome participants that want to help test and finish the JDK conversions. I'd also like to see 'helper objects' added, so a DateTimeFormatter can be used to help convert a DateTime, or a ClassLoader to convert a Class.

Feedback welcome as always!

14 comments:

  1. This looks interesting, but I think I prefer using something like a Show instance (see http://functionaljava.googlecode.com/svn/artifacts/3.0/javadoc/fj/Show.html for an example) or better yet a Bijection which represents the invertible function A => B * B => A so that I get a guarantee of compile-time type safety and avoid reflection.

    Conversion to and from some arbitrary data type (with String being just a common specialization) should really be represented as an invertible function. In Java this of course means you have to add an argument representing the bijection object to the function calls where you want to use it, which is a bit of a pain; in Scala you can use the implicits feature to get the proper bijection instance automatically injected into your method call. Now, it might be interesting to see if there's a way to use Java annotations and reflection to do the lookup and perhaps reflective instantiation and application of the appropriate bijection, but again in this case you'll miss out on the compile-time guarantees.

    ReplyDelete
  2. Stephen Colebourne20 August 2010 at 18:07

    There is an interface StringConverter which is type safe and has conversions to and from a string. The reflection is all hidden, so users never see it.

    As well as the annotation approach, you can implement the interface directly yourself, and register it. This obviously avoids all reflection, at the cost of writing a simple class.

    ReplyDelete
  3. Stephen Colebourne20 August 2010 at 18:12

    Bob, the problem is bigger than you might imagine. No-one can agree on the standard method names for starters!

    Moreover, in the JDK there are cases where the canonical String representation has changed with time. The toString method on a TimeZone isn't very helpful for example. You might say "well just change the toString", but it turns out that is frequently not backwards compatible. Thus, an annotation is useful for marking the new method which has to be added to avoid breaking compatibility.

    ReplyDelete
  4. @Bob whats with the vitriol? What have you contributed lately?

    What if you are inheriting a class that implements a fromString() method with a different contract than you want? The annotation-based method gives you a lot of type independence.

    ReplyDelete
  5. It is interesting.

    I have similar code and if there will be library witch will be more standard (had chances to get to JDK at one point) I will consider using it.

    However my case is a bit more complicated.
    I serialize to String like 'dis.127b8156-2396-4cec-ab1c-4f7feadbb62b' where everything after "dis." is UUID.
    Of course Distance will not be able to deserialize itself from such ID. I have helper class which use DAO to get whole Object.

    How this will work. I understand you don't use APT to process your annotations and generate some code - as I do.

    ReplyDelete
  6. Isn't this what java.beans.PropertyEditor is used for?

    ReplyDelete
  7. Stephen Colebourne20 August 2010 at 22:32

    Arek, The aim of this project should be very simple. So, compound decoding would not be supported. Annotations can be accessed by reflection - no APT required.

    Andy, ProperyEditor has a whole heap of other rubbish on the interface.

    ReplyDelete
  8. Stephen,

    The documentation should have some discussion of how it relates to similar facilities that are already part of Java. Adding this to java.text.Format and java.util.Formatter has the potential to add more confusion to the platform.

    Thanks,
    Curt

    ReplyDelete
  9. Hi Stephen,
    Is this the same joda-time <-> String conversion strategy employed in the jibx library?
    http://jibx.sourceforge.net/binding/date-time.html

    ReplyDelete
  10. Stephen Colebourne22 August 2010 at 21:28

    Curt, I would say that type conversion and formatting are two different things, but I'll see if I can add something to explain.

    SK, Jibx are using a different strategy from the looks of it. Sadly, type conversion is something each and every project seems to do.

    ReplyDelete
  11. Stephen,

    Spring introduced a dedicated type conversion system in version 3.0, separate from and superior to its long-standing support for JavaBean PropertyEditors. This support includes integration with Joda Time for Date/time conversion. Conversion can be driven by type as well as by annotation.

    You can read more about this particular system here:
    http://blog.springsource.com/2009/11/17/spring-3-type-conversion-and-validation/

    and:
    http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/validation.html#core-convert

    Hope this is a helpful resource for you! (I am the primary author of the system).

    Keith

    ReplyDelete
  12. Stephen Colebourne23 August 2010 at 10:05

    Keith, thanks for the links

    ReplyDelete
  13. The Spring example reminds me of the need for both localised and non localised conversions.

    ReplyDelete
  14. What about this:

    public class Foo{

    @FromType(String.class) //@FromType get type from parameter
    public static Foo parse(String str) { ... }

    @ToType(String.class) //@ToType get type from method return type
    public String getStringOutput() { ... }


    @FromType //@FromType(Bar.class)
    public static Foo parse(Bar bar) { ... }

    @ToType //@ToType(Bar.class)
    public Bar toBar() { ... }

    }

    Best Regards,
    Ali Ebrahimi

    ReplyDelete

Please be aware that by commenting you provide consent to associate your selected profile with your comment. Long comments or those with excessive links may be deleted by Blogger (not me!). All spam will be deleted.