Question

I'm trying to use MOXy to customize the naming algorithm for XML marshaling and schema generation for classes across multiple packages.

I'm following the Eclipse documentation and can get it working for a single class:

@XmlNameTransformer(com.example.test.NameGenerator.class)
public class Customer

or a single package:

@XmlNameTransformer(com.example.test.NameGenerator.class)
package com.example.test;

(an external bindings file also works, but again you can only specify package per binding file)

However, since I have objects spanned across multiple packages, I don't want to have a define the transformer for every package. Additionally I'd like to do this dynamically (i.e. the individual packages aren't known until runtime).

I've tried defining the transformer at a parent package (com.example) but this doesn't work--it looks like it needs to be the exact package where the annotated classes are.

Any ideas?

Update - more information

Maybe I should mention that the main reason I'm trying to do this so so I can have control over XML/XSD generation--and most specifically, I want complex types to be uppercase instead of the default lower (without needing to annotate every single class), like

@XmlRootElement
public class Test ...

should generate:

<Test>
...
</Test>

instead of

<test>
...
</test>

From other research I've done, the only way I've found to do this is with an XmlNameTransformer. But if there's another way and I'm barking up the wrong tree...definitely let me know!

Was it helpful?

Solution 2

Package level is the biggest scope at which you can specify MOXy/JAXB metadata. You can use the same implementation of XmlNameTransformer but you will need to specify it for each package that you want it applied to.

However, since I have objects spanned across multiple packages, I don't want to have a define the transformer for every package. Additionally I'd like to do this dynamically (i.e. the individual packages aren't known until runtime).

You can programatically create the object model that corresponds to the external mapping document. Then you can pass the instance of this when creating the JAXBContext.

OTHER TIPS

Expanding on Blaise's answer, here is what my implementation looks like. This accomplishes using a transformer for a dynamic set of packages.

import org.eclipse.persistence.jaxb.metadata.MetadataSourceAdapter;
import org.eclipse.persistence.jaxb.xmlmodel.XmlBindings;
import org.eclipse.persistence.jaxb.xmlmodel.XmlSchema;

public class NameTransformerAdapter extends MetadataSourceAdapter {

    private XmlBindings bindings;

    /**
     * @param packageName Fully qualified package name, like {@code com.example.test}
     */
    public NameTransformerAdapter(String packageName) {
            bindings = new XmlBindings();
            bindings.setPackageName(packageName);
            bindings.setXmlNameTransformer(XmlNameTransformer.class.getName());
            bindings.setXmlSchema(new XmlSchema()); // this is necessary
    }
    @Override
    public XmlBindings getXmlBindings(Map<String, ?> properties, ClassLoader classLoader) {
            return bindings;
    }
}

The transformer referenced is pretty simple:

import org.eclipse.persistence.oxm.XMLNameTransformer;

public class XmlNameTransformer implements XMLNameTransformer {

    /** Uses capitalized name of class */
    public String transformTypeName(String name) {
            return name.substring(name.lastIndexOf('.') + 1);
    }
    public String transformElementName(String name) {
            return name;
    }
    public String transformAttributeName(String name) {
            return name;
    }
    public String transformRootElementName(String name) {
            return transformTypeName(name);
    }
}

Packages are determined at runtime and are tracked as such:

List<MetadataSource> adapters = new ArrayList<MetadataSource>();
adapters.add(new NameTransformerAdapter(packageName));

Lastly, a JAXBContext can be obtained using the above adapters:

import org.eclipse.persistence.jaxb.JAXBContextFactory;
import org.eclipse.persistence.jaxb.JAXBContextProperties;

private JAXBContext getContext(Class<?>[] classes) throws JAXBException {
    Map<String, Object> properties = new HashMap<String, Object>();
    properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, adapters);
    return JAXBContextFactory.createContext(classes, properties);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top