Java Code to XML/XSD without using Annotation

2020-02-25 20:55发布

问题:

I need to marshall and unmarshall a Java class to XML. The class in not owned by me, that I cannot add anotations so that I can use JAXB.

Is there a good way to convert the Java to XML with the given contraint?

Also, thought a tool may be helpful, but I would be more intersted it there is some Java API to do the same.

回答1:

Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.

DOMAIN MODEL

I will use the following domain model for this answer. Note how there are no JAXB annotations on the model.

Customer

package forum11693552;

import java.util.*;

public class Customer {

    private String firstName;
    private String lastName;
    private List<PhoneNumber> phoneNumbers = new ArrayList<PhoneNumber>();

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public List<PhoneNumber> getPhoneNumbers() {
        return phoneNumbers;
    }

    public void setPhoneNumbers(List<PhoneNumber> phoneNumbers) {
        this.phoneNumbers = phoneNumbers;
    }

}

PhoneNumber

package forum11693552;

public class PhoneNumber {

    private String type;
    private String number;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

}

OPTION #1 - Any JAXB (JSR-222) Implementation

JAXB is configurartion by exception, this means you only need to add annotations where you want the mapping behaviour to differ from the default. Below is a link to an example demonstrating how to use any JAXB impl without annotations:

Demo

package forum11693552;

import javax.xml.bind.*;
import javax.xml.namespace.QName;

public class Demo {

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

        Customer customer = new Customer();
        customer.setFirstName("Jane");
        customer.setLastName("Doe");

        PhoneNumber workPhone = new PhoneNumber();
        workPhone.setType("work");
        workPhone.setNumber("555-1111");
        customer.getPhoneNumbers().add(workPhone);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        JAXBElement<Customer> rootElement = new JAXBElement<Customer>(new QName("customer"), Customer.class, customer);
        marshaller.marshal(rootElement, System.out);
    }

}

Output

<customer>
    <firstName>Jane</firstName>
    <lastName>Doe</lastName>
    <phoneNumbers>
        <number>555-1111</number>
        <type>work</type>
    </phoneNumbers>
</customer>

For More Information

  • http://wiki.eclipse.org/EclipseLink/Examples/MOXy/GettingStarted/TheBasics

OPTION #2 - EclipseLink JAXB (MOXy)'s External Mapping Document

If you do want to customize the mappings, then you may be interested in MOXy's external mapping document extension. A sample mapping document looks like the following:

oxm.xml

<?xml version="1.0"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
    package-name="forum11693552">
    <java-types>
        <java-type name="Customer">
            <xml-root-element />
            <java-attributes>
                <xml-element java-attribute="firstName" name="first-name" />
                <xml-element java-attribute="lastName" name="last-name" />
                <xml-element java-attribute="phoneNumbers" name="phone-number" />
            </java-attributes>
        </java-type>
        <java-type name="PhoneNumber">
            <java-attributes>
                <xml-attribute java-attribute="type" />
                <xml-value java-attribute="number" />
            </java-attributes>
        </java-type>
    </java-types>
</xml-bindings>

jaxb.properties

To enable MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html):

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

Demo

When using EclipseLink MOXy as your JAXB provider (see), you can leverage the external mapping document when you bootstrap your JAXBContext

package forum11693552;

import java.util.*;
import javax.xml.bind.*;
import javax.xml.namespace.QName;
import org.eclipse.persistence.jaxb.JAXBContextFactory;

public class Demo {

    public static void main(String[] args) throws Exception {
        Map<String, Object> properties = new HashMap<String,Object>(1);
        properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "forum11693552/oxm.xml");
        JAXBContext jc = JAXBContext.newInstance(new Class[] {Customer.class}, properties);

        Customer customer = new Customer();
        customer.setFirstName("Jane");
        customer.setLastName("Doe");

        PhoneNumber workPhone = new PhoneNumber();
        workPhone.setType("work");
        workPhone.setNumber("555-1111");
        customer.getPhoneNumbers().add(workPhone);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        JAXBElement<Customer> rootElement = new JAXBElement<Customer>(new QName("customer"), Customer.class, customer);
        marshaller.marshal(rootElement, System.out);
    }

}

Output

<?xml version="1.0" encoding="UTF-8"?>
<customer>
   <first-name>Jane</first-name>
   <last-name>Doe</last-name>
   <phone-number type="work">555-1111</phone-number>
</customer>

For More Information

  • http://blog.bdoughan.com/2010/12/extending-jaxb-representing-annotations.html
  • http://blog.bdoughan.com/2012/04/extending-jaxb-representing-metadata-as.html


回答2:

Have you looked at XStream ? It will deserialise/deserialise a standard POJO without annotations or XSDs. You can provide customisations to affect how elements appear in the XML and pretty much works out-of-the-box.



回答3:

You could write a custom XmlAdapter and annotate fields of the constrained type with a XmlJavaTypeAdapter annotation. The basics would be something like this:

public enum CannotBeAnnotated { value1, value2; }
@XmlRootElement(name="client")
public class ClientClass {
    @XmlJavaTypeAdapter(Bridge.class)
    public CannotBeAnnotated;
}
@XmlRootElement(name="representation")
public class XmlType {
    @XmlValue
    public String value;
}
public class Bridge extends XmlAdapter<XmlType, CannotBeAnnotated>{
    public XmlType marshal(CannotBeAnnotated c) {
        XmlType x=new XmlType(); 
        x.value=c.name();
        return x;
    }
    public CannotBeAnnotated unmarshall(XmlType x) {
        return CannotBeAnnotated.valueOf(x.value);
    }
}

Of course for enums this would not be useful as JAXB knows how to deal with them. I just picked an enum for simplicity so you can see the idea:

  1. Design an XML representation that you do control
  2. Write an adapter converting that Java type into the desired type
  3. Annotate "client" code referencing the adapter for the desired type
  4. Profit.