4
votes

In order to marshal jaxb classes with Apache Camel the jaxb class needs to include a XmlRootElement annotation.

When generating jaxb classes from XSD the XmlRootElement annotation might not be generated.

This will lead to an Exception during marshalling "No type converter available to convert from type: "

As soon as I add the @XmlRootElement manually, everything works fine, but since these Jaxb classes are generated, adding the anntotation manually is no option.

According to the Camel documentation in such a case, the JaxbDataFormat can be set to 'fragement(true)

JaxbDataFormat jaxbMarshal = new JaxbDataFormat();
jaxbMarshal.setContextPath(ObjectFactory.class.getPackage().getName());
jaxbMarshal.setFragment(true);

Unfortunately I still get the same exception.

Is there a way to configure JaxbDataFormat different, i.e. to define the JAXBElement which is the root element, like I would do in Java

marshaller.marshal( new JAXBElement( new QName("uri","local"),
   MessageType.class, messageType ));

or is there another strategy available to get the XML marshalled?

EDIT the used route :

 from("file://inbox").unmarshal(jaxbDataFormat)
.marshal(jaxbDataFormat).to("file://outbox");

the stacktrace:

java.io.IOException: org.apache.camel.NoTypeConversionAvailableException: No type converter available to convert from type: com.xyz.AddressType to the required type: java.io.InputStream with value com.xyz.AddressType@32317e9d at org.apache.camel.converter.jaxb.JaxbDataFormat.marshal(JaxbDataFormat.java:148) ~[camel-jaxb-2.16.0.jar:2.16.0] at org.apache.camel.processor.MarshalProcessor.process(MarshalProcessor.java:83) ~[camel-core-2.16.0.jar:2.16.0] at

...

[na:1.8.0_25] at java.lang.Thread.run(Thread.java:745) [na:1.8.0_25] Caused by: org.apache.camel.NoTypeConversionAvailableException: No type converter available to convert from type: com.xyz.AddressType to the required type: java.io.InputStream with value com.xyz.AddressType@32317e9d at org.apache.camel.impl.converter.BaseTypeConverterRegistry.mandatoryConvertTo(BaseTypeConverterRegistry.java:185) ~[camel-core-2.16.0.jar:2.16.0] at

...

4
"No type converter available.." is not thrown from the DataFormat, but from the TypeConverter. can you show the route you are using ?Jérémie B
And what about the real error and a stacktrace ?Jérémie B
Have you added the jaxb.index file that points to your jaxb class?Souciance Eqdam Rashti
There is this ticket to improve on this if you have an ObjectFactory class: issues.apache.org/jira/browse/CAMEL-9649Claus Ibsen

4 Answers

1
votes

I experienced the equivalent behaviour with JaxB (@XmlRootElement annotation not present in the generated class), and I suppose it comes from the way the root element is defined in the XML schema.

For example:

<xsd:element name="DiffReport" type="DiffReportType" />
<xsd:complexType name="DiffReportType">
    ...
</xsd:complexType>

it will generate you the DiffReportType class without the @XmlRootElement annotation. But if you directly define your root element as following, you'll get the annotation set in your generated class (the name of the root class is then DiffReport in my example).

<xsd:element name="DiffReport">
    <xsd:complexType>
        ...

Note: I used the first way to define the complex types in my schema for class name consistency.

1
votes

In Camel 2.17, the @XmlRootElement was not required. As of 2.21, it is. Unless...

The class org.apache.camel.converter.jaxb.FallBackTypeConverter changed it's implementation from:

protected <T> boolean isJaxbType(Class<T> type) {
    return hasXmlRootElement(type) || JaxbHelper.getJaxbElementFactoryMethod(camelContext, type) != null;
}

To:

protected <T> boolean isJaxbType(Class<T> type) {
    if (isObjectFactory()) {
        return hasXmlRootElement(type) || JaxbHelper.getJaxbElementFactoryMethod(camelContext, type) != null;
    } else {
        return hasXmlRootElement(type);
    }
}

By default the isObjectFactory() method returns false. If you set the property CamelJaxbObjectFactoryon your CamelContext to true. then the JaxbHelper.getJaxbElementFactoryMethod(camelContext, type) will return true and the deserialization works again as before without the need for an @XmlRootElement. For completeness:

<camelContext xmlns="http://camel.apache.org/schema/spring" id="camelContext">
    <properties>
        <property key="CamelJaxbObjectFactory" value="true"/>
    </properties>
</camelContext>
0
votes

You can use the "partClass" option of the jaxb data format of camel. Your question is answered in the camel docs for jaxb, which describes how to marshall XML fragments (or XML generated without the XmlRootElement annotation).

0
votes

Use partClass and provide the actual class name to which you wish to marshall. In case of marshalling you also have to provide the partNamespace which is the target namespace of the desired XML object.