Type Converter

Its very common when routing messages from one endpoint to another to need to convert the body payloads from one type to another such as to convert to and from the following common types

The Message interface defines a helper method to allow conversions to be done via the getBody(Class) method.

So in an endpoint you can convert a body to another type via

Message message = exchange.getIn();
Document document = message.getBody(Document.class);

How Type Conversion works

The type conversion strategy is defined by the TypeConverter interface which can be customized on a CamelContext.

The default implementation, DefaultTypeConverter uses pluggable strategies to load type converters via TypeConverterLoader. The default strategy, AnnotationTypeConverterLoader uses a discovery mechanism to find converters.

New in Camel 1.5

The default implementation, DefaultTypeConverter now throws a NoTypeConversionAvailableException if a suitable conversion cannot be found (CAMEL-84). The semantical ambiguity of null (both valid result and indication of no conversion found) is now resolved, but this may impact existing code in that it should now catch the exception instead of checking for null.

TypeConverterRegistry

New in Camel 2.0

Exposed the TypeConverterRegistry from CamelContext so end users more easily will be able to add type converters at runtime. This is also usable in situations where the default discovering of type converters fails, on platforms with classloading issues.

To access the registry you get it from the CamelContext

   CamelContext context = ...
   context.getTypeConverterRegistry()

Add type converter at runtime

The following sample demonstrates how to add a type converter at runtime:

        // add our own type converter manually that converts from String -> MyOrder using MyOrderTypeConverter
        context.getTypeConverterRegistry().addTypeConverter(MyOrder.class, String.class, new MyOrderTypeConverter());

And our type converter is implemented as:

    private static class MyOrderTypeConverter extends TypeConverterSupport {

        @SuppressWarnings("unchecked")
        public <T> T convertTo(Class<T> type, Exchange exchange, Object value) {
            // converter from value to the MyOrder bean
            MyOrder order = new MyOrder();
            order.setId(Integer.parseInt(value.toString()));
            return (T) order;
        }
    }

And then we can convert from String to MyOrder as we are used to with the type converter:

        MyOrder order = context.getTypeConverter().convertTo(MyOrder.class, "123");

Discovering Type Converters

The AnnotationTypeConverterLoader will search the classpath for a file called META-INF/services/org/apache/camel/TypeConverter. The contents are expected to be comma separated package names. These packages are then recursively searched for any objects with the @Converter annotation. Then any method marked with @Converter is assumed to be a conversion method; where the parameter is the from value and the return is the to value.

e.g. the following shows how to register a converter from File -> InputStream

@Converter
public class IOConverter {

    @Converter
    public static InputStream toInputStream(File file) throws FileNotFoundException {
        return new BufferedInputStream(new FileInputStream(file));
    }
}

Static methods are invoked; non-static methods require an instance of the converter object to be created (which is then cached). If a converter requires configuration you can plug in an Injector interface to the DefaultTypeConverter which can construct and inject converter objects via Spring or Guice.

We have most of the common converters for common Java types in the org.apache.camel.converter package and its children.

Discovering Fallback Type Converters

Available in Camel 2.0
The AnnotationTypeConverterLoader has been enhanced to also look for methods defined with a @FallbackConverter annotation, and register it as a fallback type converter.

Fallback type converters is used as a last resort of hopes for converting a given value to another type. Its used when the regular type converters give up.
The fallback converters is also meant for a broader scope so its method signature is a bit different:

    @FallbackConverter
    public static <T> T convertTo(Class<T> type, Exchange exchange, Object value, TypeConverterRegistry registry)

Or you can use the non generic signature.

    @FallbackConverter
    public static Object convertTo(Class type, Exchange exchange, Object value, TypeConverterRegistry registry)

And the method name can be anything (convertTo is not required as a name), so it can be named convertMySpecialTypes if you like.
The Exchange parameter is optional, just as its with the regular @Converter methods.

The purpose with this broad scope method signature is allowing you to control if you can convert the given type or not. The type parameter holds the type we want the value converted to. Its used internally in Camel for wrapper objects so we can delegate the type convertions to the body that is wrapped.

For instance in the method below we will handle all type conversions that is based on the wrapper class GenericFile and we let Camel do the type conversions on its body instead.

    @FallbackConverter
    public static <T> T convertTo(Class<T> type, Exchange exchange, Object value, TypeConverterRegistry registry) {
        // use a fallback type converter so we can convert the embedded body if the value is GenericFile
        if (GenericFile.class.isAssignableFrom(value.getClass())) {
            GenericFile file = (GenericFile) value;
            Class from = file.getBody().getClass();
            TypeConverter tc = registry.lookup(type, from);
            if (tc != null) {
                Object body = file.getBody();
                return tc.convertTo(type, exchange, body);
            }
        }
        
        return null;
    }

Writing your own Type Converters

Use FQN

In Camel 2.8 the TypeConverter file now supports specifying the FQN class name. This is recommended to be used. See below for more details

You are welcome to write your own converters. Remember to use the @Converter annotations on the classes and methods you wish to use. Then add the packages to a file called META-INF/services/org/apache/camel/TypeConverter in your jar. Remember to make sure that :-

Examples of TypeConverter file

The file in the JAR: META-INF/services/org/apache/camel/TypeConverter contains the following line(s)

com.foo
com.bar

Each line in the file is package name. This tells Camel to go scan those packages for any classes which has been annotated with the @Converter.

Improved TypeConverter by using FQN class names

Available as of Camel 2.8
In Camel 2.8 we improved the type converter loader to support specifying the FQN class name of the converter classes. This has a much better advantage as it avoids having to scan packages for @Converter classes. Instead it loads the @Converter class directly. This is highly recommend approach to use going forward.

Examples of TypeConverter file

The file in the JAR: META-INF/services/org/apache/camel/TypeConverter contains the following line(s) for FQN class names

com.foo.MyConverter
com.bar.MyOtherConverter
com.bar.YetOtherConverter

As you can see each line in the file now contains a FQN class name. This is the recommended approach.

Encoding support for byte[] and String Conversion

Available in Camel 1.5

Since Java provides converting the byte[] to String and String to byte[] with the charset name parameter. You can define the charset name by setting the exchange property name Exchange.CHARSET_NAME with the charset name, such as "UTF-8" or "iso-8859-1".

Exchange parameter

Available in Camel 1.5

The type converter accepts the Exchange as an optional 2nd parameter. This is usable if the type converter for instance needs information from the current exchange. For instance combined with the encoding support its possible for type converters to convert with the configured encoding. An example from camel-core for the byte[] -> String converter:

    @Converter
    public static String toString(byte[] data, Exchange exchange) {
        if (exchange != null) {
            String charsetName = exchange.getProperty(Exchange.CHARSET_NAME, String.class);
            if (charsetName != null) {
                try {
                    return new String(data, charsetName);
                } catch (UnsupportedEncodingException e) {
                    LOG.warn("Can't convert the byte to String with the charset " + charsetName, e);
                }
            }
        }
        return new String(data);
    }