3

I need create/read xml file using default namespace:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xmlBoo xmlns="http://www.example2.org/boo">
    <customer>
        <address>
            <street>Wall Street</street>
        </address>
        <id>1</id>
        <name>John</name>
    </customer>
    <someSpecificField>Specific data in Boo</ns2:someSpecificField>
</xmlBoo>

but I'm getting:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:xmlBoo xmlns:ns2="http://www.example2.org/boo">
    <ns2:customer>
        <address>
            <street>Wall Street</street>
        </address>
        <id>1</id>
        <name>John</name>
    </ns2:customer>
    <ns2:someSpecificField>Specific data in Boo</ns2:someSpecificField>
</ns2:xmlBoo>

I know about package level metadata, but this is not working in complex package structure:

project structure

I have defined model classes like Address:

package example.model;

public class Address {
    private String street;

Customer:

package example.model;

public class Customer {
    private long id;
    private String name;
    private Address address;

The parent class for common fields:

package example.xml;

@XmlTransient
public class Xml {
    private Customer customer;

Then specific classes which holds data/structure of concrete xml XmlBoo:

package example.xml.boo;

@XmlRootElement
public class XmlBoo extends Xml {
    private String someSpecificField;

XmlFoo:

package example.xml.foo;

@XmlRootElement
public class XmlFoo extends Xml {}

package-info.java is included in two mentioned packages example.xml.boo:

@XmlSchema(
        namespace = "http://www.example2.org/boo",
        elementFormDefault = XmlNsForm.QUALIFIED)
package example.xml.boo;

and example.xml.foo:

@XmlSchema(
    namespace = "http://www.example2.org/foo",
    elementFormDefault = XmlNsForm.QUALIFIED)

package example.xml.foo;

And finally main method:

package example;

public class Demo {

    public static void main(String... args) {
        generateBoo();
        generateFoo();
    }

    public static void generateBoo() {
        try {
            JAXBContext jc = JAXBContext.newInstance(XmlBoo.class);
            Marshaller m = jc.createMarshaller();
            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

            XmlBoo xmlBoo = new XmlBoo();

            Customer customer = new Customer();
            customer.setId(1);
            customer.setName("John");
            Address address = new Address();
            address.setStreet("Wall Street");
            customer.setAddress(address);
            xmlBoo.setCustomer(customer);
            xmlBoo.setSomeSpecificField("Specific data in Boo");

            m.marshal(xmlBoo, System.out);

        } catch (JAXBException e) {
            e.printStackTrace();
        }
    }

    public static void generateFoo() {
        try {
            JAXBContext jc = JAXBContext.newInstance(XmlFoo.class);
            Marshaller m = jc.createMarshaller();
            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

            XmlFoo xmlFoo = new XmlFoo();
            Customer customer = new Customer();
            customer.setId(1);
            customer.setName("John");
            Address address = new Address();
            address.setStreet("Wall Street");
            customer.setAddress(address);
            xmlFoo.setCustomer(customer);

            m.marshal(xmlFoo, System.out);

        } catch (JAXBException e) {
            e.printStackTrace();
        }
    }
}

I've tried both solutions like here and also without success.

  • It is possible remove & rename prefix if I have all classes in one package (and one package-info file)
  • It is possible rename but NOT remove prefix if I have complex package structure

Is there solution how I can remove ns2 prefix? I'm using JDK7.

9
  • Why do you need to generate it without the ns2 tags? It's semantically the same, in that each element is still in the same qualified namespace. If the code consuming this cares, it's broken IMO. Commented Apr 7, 2013 at 18:07
  • 1
    If you have a schema you can set elementFormDefault to unqualified and set that schema on the marshaller. Otherwise this may be of help. Commented Apr 7, 2013 at 18:09
  • @JonSkeet because I'm client of this xml without ns2, I can't change it. Commented Apr 7, 2013 at 19:26
  • @bmorris591 I don't have schema. Commented Apr 7, 2013 at 21:08
  • @Ziletka: What do you mean by "I'm client"? Do you understand that anything handling the XML properly should accept the version with the namespace alias? Commented Apr 8, 2013 at 5:44

3 Answers 3

3

Solution how get (write & read xml) the needed result:

<?xml version="1.0" encoding="UTF-8"?>
<xmlBoo xmlns="http://www.example.org/boo" xmlns:c="http://www.example.org/customer" xmlns:a="http://www.example.org/address" xmlns:h="http://www.example.org/header">
   <h:header>
      <h:id>101</h:id>
   </h:header>
   <c:customer>
      <c:id>1</c:id>
      <c:name>Yen</c:name>
      <a:address>
         <a:street>Long street</a:street>
      </a:address>
   </c:customer>
   <someBooSpecificField>Specific data in Boo</someBooSpecificField>
</xmlBoo>
  • for root element and its "simple" children is used default namespace (without prefix)
  • for complex (objects in java) children are used different namespaces (mapped to different prefixes)
  • model classes are in different packages

So here is the solution:

enter image description here

Define MOXy implementation of JAXB, file: jaxb.properties

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

Create abstract class for common fields, define namespace for object, file Xml.java

package example.xml;

@XmlTransient
public abstract class Xml {

    private Header header;
    private Customer customer;

    @XmlElement(namespace="http://www.example.org/header")
    public Header getHeader() {
        return header;
    }

    public void setHeader(Header header) {
        this.header = header;
    }

    @XmlElement(namespace="http://www.example.org/customer")
    public Customer getCustomer() {
        return customer;
    }

    public void setCustomer(Customer customer) {
        this.customer = customer;
    }
}

Create "root" class, XmlBoo.java

package example.xml.boo;

@XmlRootElement
@XmlType(propOrder = {"header", "customer", "someBooSpecificField"})
public class XmlBoo extends Xml {

    private String someBooSpecificField;

    // getter & setter
}

Set namespace and QUALIFIED for "root" class, file: example.xml.boo.package-info.java

@XmlSchema(
        namespace = "http://www.example.org/boo",
        elementFormDefault = XmlNsForm.QUALIFIED)

package example.xml.boo;

Set QUALIFIED to generate prefix for children (the namespace will be overridden by namespace defined on the class, but it must be defined), file: example.model.package-info.java

@XmlSchema(
        namespace = "http://www.example.org",
        elementFormDefault = XmlNsForm.QUALIFIED)

package example.model;

Create Header.java

package example.model;

@XmlType(namespace = "http://www.example.org/header")
public class Header {

    private long id;

    // getter & setter
}

Create Customer.java

package example.model;

@XmlType(namespace = "http://www.example.org/customer", propOrder = {"id", "name", "address"})
public class Customer {

    private long id;
    private String name;
    private Address address;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    @XmlElement(namespace="http://www.example.org/address")
    public Address getAddress() {
        return address;
    }

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

Create Address.java

package example.model;

@XmlType(namespace = "http://www.example.org/address")
public class Address {

    private String street;

    // getter & setter
}

Create MyNamespacePrefixMapper.java by extending org.eclipse.persistence.oxm.NamespacePrefixMapper

package example;
import org.eclipse.persistence.oxm.NamespacePrefixMapper;

public class MyNamespacePrefixMapper extends NamespacePrefixMapper {

    private static final String BOO_PREFIX = ""; // DEFAULT NAMESPACE
    private static final String BOO_URI = "http://www.example.org/boo";
    private static final String FOO_PREFIX = ""; // DEFAULT NAMESPACE
    private static final String FOO_URI = "http://www.example.org/foo";
    private static final String HEADER_PREFIX = "h";
    private static final String HEADER_URI = "http://www.example.org/header";
    private static final String CUSTOMER_PREFIX = "c";
    private static final String CUSTOMER_URI = "http://www.example.org/customer";
    private static final String ADDRESS_PREFIX = "a";
    private static final String ADDRESS_URI = "http://www.example.org/address";

    @Override
    public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {

        switch (namespaceUri) {
            case BOO_URI:
                return BOO_PREFIX;
            case FOO_URI:
                return FOO_PREFIX;
            case HEADER_URI:
                return HEADER_PREFIX;
            case CUSTOMER_URI:
                return CUSTOMER_PREFIX;
            case ADDRESS_URI:
                return ADDRESS_PREFIX;
            default:
                return null;
        }
    }
}

Create XML

public static void generateBoo() {
    try {
        JAXBContext jc = JAXBContext.newInstance(XmlBoo.class);
        Marshaller m = jc.createMarshaller();
        m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        m.setProperty(MarshallerProperties.NAMESPACE_PREFIX_MAPPER, new MyNamespacePrefixMapper());

        XmlBoo xmlBoo = new XmlBoo();
        Header header = new Header();
        header.setId(101);
        xmlBoo.setHeader(header);

        Customer customer = new Customer();
        customer.setId(1);
        customer.setName("Yen");
        Address address = new Address();
        address.setStreet("Long street");
        customer.setAddress(address);
        xmlBoo.setCustomer(customer);

        xmlBoo.setSomeBooSpecificField("Specific data in Boo");

        m.marshal(xmlBoo, System.out);
        m.marshal(xmlBoo, new File("xml_boo.xml"));

    } catch (JAXBException e) {
        e.printStackTrace();
    }
}

Read XML

public static void readBoo() {

    Object element = null;

    try {
        JAXBContext jc = JAXBContext.newInstance(XmlBoo.class);
        Unmarshaller u = jc.createUnmarshaller();
        element = u.unmarshal(new File("xml_boo.xml"));

    } catch (JAXBException e) {
        e.printStackTrace();
    }

    if (element instanceof XmlBoo) {
        XmlBoo xmlBoo = (XmlBoo) element;
        Customer customer = xmlBoo.getCustomer();

        System.out.println("INFO | xmlBoo field:  [" + xmlBoo.getSomeBooSpecificField() + "]");
        System.out.println("INFO | customer name: [" + customer.getName() + "]");
        System.out.println("INFO | address street: [" + customer.getAddress().getStreet() + "]");

    }
}
Sign up to request clarification or add additional context in comments.

Comments

2

I used EclipseLink MOXy JAXB implementation instead of RI Metro JAXB and now it works. So it looks that in Metro is bug.

Perfect tutorial by Blaise Doughan: JAXB & Namespace prefixes

Comments

1

You will need to have a package-info annotation with a @XmlSchema annotation for each package in your domain model each specifying the same namespace qualification to get the desired XML.

4 Comments

Problem is that I can't even I wan't use the same namespace. The same domain classes are used in different XMLs eg. namespace = "example2.org/boo" and namespace = "example2.org/foo". Is that wrong thought? Also I wan't copy & paste the same package-info into all subpackages.
@Ziletka - So for a single domain model you want the ability to marshal it out to one space one time and another namespace another time?
Yes. Our system is client and is communicating with some old system (in real with few systems). And XMLs like <xmlBoo xmlns="example2.org/boo">...</xmlBoo> and <xmlFoo xmlns="example2.org/foo">...</xmlFoo> and others almost contains some mix of domain classes.
I've tried both solution like here and also without success. It is possible rename prefix, but not remove it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.