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.

Community
  • 1
  • 1
sasynkamil
  • 859
  • 2
  • 12
  • 23
  • 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. – Jon Skeet Apr 07 '13 at 18:07
  • 1
    If you have a schema you can set `elementFormDefault` to `unqualified` and set that schema on the marshaller. Otherwise [this](http://stackoverflow.com/questions/4873429/controlling-namespace-prefixes-in-jaxb) may be of help. – Boris the Spider Apr 07 '13 at 18:09
  • @JonSkeet because I'm client of this xml without ns2, I can't change it. – sasynkamil Apr 07 '13 at 19:26
  • @bmorris591 I don't have schema. – sasynkamil Apr 07 '13 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? – Jon Skeet Apr 08 '13 at 05:44
  • @JonSkeet Our system is "calling" external Web Service Server. And they are accepting just default namespaces. I don't understand what do you exactly mean by "should accept". It is not general behavior, I it have to be programmed to accept some call in different way (e.g. with namespace and also with default namespace). And if your application is communicating with a lot of external (old) systems, you have to adapt. – sasynkamil Apr 08 '13 at 09:21
  • @Ziletka: It *should accept* it in that if it's expecting an element with a namespace URI of `"http://www.example2.org/boo"` and a local name of `xmlBoo`, then it shouldn't matter how that pair is represented - whether it's using namespace defaulting or an explicit namespace alias. It sounds like the web service you're talking to is broken, and you should contact the person running it. Obviously it *may* not be possible to fix it, but fixing it at the server side would be better than all clients having to work around it being incorrect. – Jon Skeet Apr 08 '13 at 09:23
  • @JonSkeet I still think that problem is on our site because: 1) External system is using [standart](http://www.w3schools.com/xml/xml_namespaces.asp) "default namespace", 2) If I use my implementation mentioned in question to generate xml file and then I manually remove ns2 prefix from file and try to read back, I get Customer which is null. 3) If I put all classes in the one package I can remove (by prefix="") or rename (prefix="someName") prefix. But if I use some complex package structure I can just rename prefix NOT remove. So from my point of view it looks that problem is caused by JAXB – sasynkamil Apr 08 '13 at 10:19
  • @Ziletka: Yes, it's fine to use default namespaces - but it *should* also be fine to use them explicitly with a namespace alias. The fully-qualified ID is the same. If I can't convince you of that, then I don't think I'm going to be of any use on this question... – Jon Skeet Apr 08 '13 at 10:26

3 Answers3

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() + "]");

    }
}
sasynkamil
  • 859
  • 2
  • 12
  • 23
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

sasynkamil
  • 859
  • 2
  • 12
  • 23
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.

bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • 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 = "http://www.example2.org/boo" and namespace = "http://www.example2.org/foo". Is that wrong thought? Also I wan't copy & paste the same package-info into all subpackages. – sasynkamil Apr 07 '13 at 19:46
  • @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? – bdoughan Apr 07 '13 at 19:52
  • Yes. Our system is client and is communicating with some old system (in real with few systems). And XMLs like ... and ... and others almost contains some mix of domain classes. – sasynkamil Apr 07 '13 at 20:08
  • I've tried both solution like [here](http://stackoverflow.com/questions/14601877/using-jdks-jaxb-without-ns2-prefix?rq=1) and also without success. It is possible rename prefix, but not remove it. – sasynkamil Apr 07 '13 at 21:05