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!
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.
ReplyDeleteConversion 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.
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.
ReplyDeleteAs 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.
Bob, the problem is bigger than you might imagine. No-one can agree on the standard method names for starters!
ReplyDeleteMoreover, 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.
@Bob whats with the vitriol? What have you contributed lately?
ReplyDeleteWhat 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.
It is interesting.
ReplyDeleteI 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.
Isn't this what java.beans.PropertyEditor is used for?
ReplyDeleteArek, 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.
ReplyDeleteAndy, ProperyEditor has a whole heap of other rubbish on the interface.
Stephen,
ReplyDeleteThe 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
Hi Stephen,
ReplyDeleteIs this the same joda-time <-> String conversion strategy employed in the jibx library?
http://jibx.sourceforge.net/binding/date-time.html
Curt, I would say that type conversion and formatting are two different things, but I'll see if I can add something to explain.
ReplyDeleteSK, Jibx are using a different strategy from the looks of it. Sadly, type conversion is something each and every project seems to do.
Stephen,
ReplyDeleteSpring 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
Keith, thanks for the links
ReplyDeleteThe Spring example reminds me of the need for both localised and non localised conversions.
ReplyDeleteWhat about this:
ReplyDeletepublic 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