4

I'm using the org.jvnet.jaxb2.maven2:maven-jaxb2-plugin to create POJOs from XSD schema files.

Now I want to insert something like a custom setter. It should trim all Strings and should remove certain characters.

Do you know how to do this?


The XJB file:

<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings version="2.0" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc">
    <jaxb:bindings schemaLocation="my-schema-xml4.xsd" node="/xs:schema">
        <xjc:javaType name="java.lang.String" adapter="my.StringAdapter" />
    </jaxb:bindings>
</jaxb:bindings>

Solution for binding Java types:

<?xml version="1.0" encoding="UTF-8" ?>
<bindings version="2.0" xmlns="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc">
    <bindings schemaLocation="mySchema-xml4.xsd" node="/xs:schema">
        <globalBindings>
            <xjc:javaType name="java.lang.String" xmlType="xs:string"
                adapter="com.name.MyAdapter" />
                    <xjc:javaType name="java.lang.String" xmlType="xs:anySimpleType"
                adapter="com.name.MyAdapter" />
        </globalBindings>
    </bindings>
</bindings>

But the @XmlJavaTypeAdapter still isn't added to the content property in nodes with mixed content, although the property has the type java.lang.String.

4 Answers4

1

I think the best way to achieve this is to implement an own XmlAdapter and configure it via the property customization. You can do this with a standard jaxb:property customization or with the annotate plugin.

jaxb:property:

  <jaxb:property>
    <jaxb:baseType>
      <xjc:javaType name="java.lang.String"
        adapter="com.acme.foo.MyAdapter"/>
    </jaxb:baseType>
  </jaxb:property>

Annotate plugin:

  <annox:annotate target="field">
    <annox:annotate
      annox:class="javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter"
      value="com.acme.foo.MyAdapter"/>
  </annox:annotate>

See the sample project here:

https://svn.java.net/svn/jaxb2-commons~svn/basics/trunk/tests/one/

lexicore
  • 42,748
  • 17
  • 132
  • 221
  • I've implemented the XmlAdapter, but I don't know how to append it. Please see my XJB file in the edited question. –  Sep 08 '11 at 13:36
  • 1
    Please re-read my answer, `jaxb:property` is the way to go. There's also a link to a sample project - have you checked that? – lexicore Sep 08 '11 at 14:00
  • I've re-read your answer and also checked the sample project, but I don't know where to append the `jaxb:property` when I want it to affect all Strings in the schema. –  Sep 09 '11 at 08:21
  • Ah, all string. I have to check. – lexicore Sep 09 '11 at 08:22
  • I've found out how to parse types (see my edited answer), but I still can't parse values in nodes. –  Sep 09 '11 at 09:12
  • > But the @XmlJavaTypeAdapter still isn't added to the content > property in nodes with mixed content, although the property has the > type java.lang.String. These are probably just a few cases, try to address them with the annotate plugin (one of the options I've listed). – lexicore Sep 09 '11 at 12:47
0

If your XSD will not change in the future just generate the POJOs, comment out the JAXB plugin and modify the source by hand.

If your XSD changes rarely you still can use this solution with a version control system. Commit the generated source, modify the POJOs and commit the modifications. Next time when the XSD is modified generate a patch between the two commits, generate the POJOs from the new XSD and apply the patch. I found this solution a lot simpler in cases where the XSD almost never changes.

palacsint
  • 28,416
  • 10
  • 82
  • 109
  • Unfortunately I get the exception `javax.xml.stream.FactoryConfigurationError: Provider com.bea.xml.stream.MXParserFactory not found` when I want to create a new `XMLInputFactory`. –  Sep 16 '11 at 06:47
0

An alternate approach would be to use a StAX StreamReaderDelegate to manipulate the XML text before it was received by the JAXB implementation.

Demo

Your code would look something like the following. You would implement a StreamReaderDelegate to manipulate the text returned from the text events:

package forum7329881;

import java.io.FileInputStream;
import javax.xml.bind.*;
import javax.xml.stream.*;
import javax.xml.stream.util.StreamReaderDelegate;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Customer.class);

        XMLInputFactory xif = XMLInputFactory.newInstance();
        XMLStreamReader xsr = xif.createXMLStreamReader(new FileInputStream("src/forum7329881/input.xml"));
        xsr = new MyStreamReaderDelegate(xsr);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        Customer customer = (Customer) unmarshaller.unmarshal(xsr);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(customer, System.out);
    }

    private static class MyStreamReaderDelegate extends StreamReaderDelegate {

        public MyStreamReaderDelegate(XMLStreamReader xsr) {
            super(xsr);
        }


        @Override
        public String getText() {
            return super.getText().trim();
        }

        @Override
        public char[] getTextCharacters() {
            return getText().toCharArray();
        }

        @Override
        public int getTextLength() {
            return getText().length();
        }

        @Override
        public int getTextStart() {
            return 0;
        }

    }

}

input.xml

Below is sample input that contains whitespace in the text nodes:

<customer Id="1">
    <name>    Jane Doe    </name>
    <address>
        <street>    123 A Street    </street>
    </address>
</customer>

Output

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
    <address>
        <street>123 A Street A</street>
    </address>
    <name>Jane Doe</name>
</customer>

Customer

package forum7329881;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Customer {

    private String name;
    private Address address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

}

Address

package forum7329881;

public class Address {

    private String street;

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

}

For More Information

Below is a link to a more detailed example where I used a StAX StreamReaderDelegate to support case insensitive unmarshalling:

bdoughan
  • 147,609
  • 23
  • 300
  • 400
0

In my opinion, you should use AOP, Pick Spring AOP, for instance, Intercept the Adapter methods to have trim / strip logic. In fact this can now be a generic logic that would apply to all string types. If this sounds convincing, I can further help with the code