1
votes

Facing issues with unmarshalling using JAXB. I need to use multiple namespaces. The Java classes are generated for the XSDs provided by a third party. So I do not want to specify the namespace at XMLRootElement in the Java classes and do not want to manually change multiple classes.

The marshalling logic as below:

private <R extends BasicResponseType, T extends BasicRequestType> R doPost(T request, String requestname) throws Exception {
    if (jaxbContext == null)
        jaxbContext = JAXBContext.newInstance(TokenRequest.class, TokenResponse.class,
                BasicResponseType.class, GeneralErrorResponse.class);
    Marshaller marshaller = jaxbContext.createMarshaller();
    marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
    Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
    logXML(marshaller, request);
    // POST to baseURL/requestname and show response
    HttpURLConnection conn = openConnection("/" + requestname);

    OutputStream os = conn.getOutputStream();
    marshaller.marshal(request, os);
    os.flush();
    // Normaler Output oder Error liefern, je nach HTTP Response
    InputStream is = null;
    boolean ok = true;
    if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
        is = conn.getErrorStream();
        ok = false;
    } else {
        is = conn.getInputStream();
    }
    R response = (R) unmarshaller.unmarshal(new StreamSource(is));
    is.close();
    conn.disconnect();
    logXML(marshaller, response);
    if (ok) {
        return response;
    } else {
        throw new Exception(getMessages((GeneralErrorResponse) response));
    }
}

The xmlelement class TokenRequest.java

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "")
@XmlRootElement(name = "TokenRequest")
public class TokenRequest
extends BasicInRequestType{ }

BasicInRequestType.java

package exp._3_0.api;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlType;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "BasicInRequestType", propOrder = {
"software"
})
@XmlSeeAlso({
TokenRequest.class    
})
public class BasicInRequestType
extends BasicRequestType {

@XmlElement(required = true)
protected SoftwareType software;

/**
 * Gets the value of the software property.
 * 
 * @return
 *     possible object is
 *     {@link SoftwareType }
 *     
 */
public SoftwareType getSoftware() {
    return software;
}

/**
 * Sets the value of the software property.
 * 
 * @param value
 *     allowed object is
 *     {@link SoftwareType }
 *     
 */
public void setSoftware(SoftwareType value) {
    this.software = value;
}}

BasicRequestType.java

package exp._3_0.api;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlType;
@XmlAccessorType(XmlAccessType.FIELD)
propOrder = {
"header",
"user"
})
@XmlSeeAlso({
    BasicInRequestType.class
})
public class BasicRequestType {

@XmlElement(required = true)
protected BasicHeaderType header;
@XmlElement(required = true)
protected UserHeaderType user;
@XmlType(name = "BasicRequestType", namespace = "http://foo/1.0/common", 
/**
 * Gets the value of the header property.
 * 
 * @return
 *     possible object is
 *     {@link BasicHeaderType }
 *     
 */
public BasicHeaderType getHeader() {
    return header;
}

/**
 * Sets the value of the header property.
 * 
 * @param value
 *     allowed object is
 *     {@link BasicHeaderType }
 *     
 */
public void setHeader(BasicHeaderType value) {
    this.header = value;
}

/**
 * Gets the value of the user property.
 * 
 * @return
 *     possible object is
 *     {@link UserHeaderType }
 *     
 */
public UserHeaderType getUser() {
    return user;
}

/**
 * Sets the value of the user property.
 * 
 * @param value
 *     allowed object is
 *     {@link UserHeaderType }
 *     
 */
public void setUser(UserHeaderType value) {
    this.user = value;
}}

XML Output :

<TokenRequest xmlns:common="http://foo/1.0/common" xmlns:ns4="http://foo/3.0/api" xmlns:base="http://foo/3.0/base">
    <common:header>
        <common:requestId></common:requestId>
        <common:timestamp></common:timestamp>
    </common:header>
    <common:user>
        <common:login></common:login>
        <common:passwordHash></common:passwordHash>
    </common:user>
    <software>
        <softwareId></softwareId>
        <softwareName></softwareName>
    </software>
</TokenRequest>

I have specifies the prefix in package-info.java

@javax.xml.bind.annotation.XmlSchema(elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED, xmlns = {
@javax.xml.bind.annotation.XmlNs(namespaceURI = "http://foo/3.0/api", prefix = ""),
@javax.xml.bind.annotation.XmlNs(namespaceURI = "http://foo/3.0/base", prefix = "base"), 
@javax.xml.bind.annotation.XmlNs(namespaceURI = "http://foo/1.0/common", prefix = "common") })
package exp._3_0.api;

The TokenRequest element actually refers to the namespace = "http://foo/3.0/api". In the xml output the TokenRequest does not have the any prefix which is correct but the xmlns is set to ns4 which is causing the below error.

Request body contains on line: [1] and column: [182] error: [unexpected element (uri:"", local:"TokenRequest"). Expected elements are <{http://foo/3.0/api}TokenRequest>]

Even after specifying the prefix = "" for namespace "http://foo/3.0/api" in package-info, in the output its still appending as ns4. Please help how to fix remove the ns4.

1
Please explicitly set the namespace in: @XmlRootElement(name = "TokenRequest", namespace="http://foo/3.0/api") public class TokenRequest ... (?) (and parents!?)xerx593
Alternatively you should add namespace="http://foo/3.0/api" to your @XmlSchema annotation. In the current form TokenRequest's namespace in "" (i.e. no namespace).Piotr P. Karwasz
@xerx593 thanks for the suggestion. But as mentioned before i do not want to manually add the namespace to XMLRootElement as there many classes which i will need to modify. I am looking for a generic solution for all the classes. Is there any other way?Juhi Vhadadi
@PiotrP.Karwasz I have already defined the namespace to "" in the package-inforJuhi Vhadadi
@JuhiVhadadi: can you edit the question and add the modified package-info.java? All we can see is @XmlNs(namespaceURI = "http://foo/3.0/api", prefix = ""), which only sets the preferred prefix for http://foo/3.0/api. There is no namespace="..." attribute (which sets the namespace for the whole package).Piotr P. Karwasz

1 Answers

0
votes

As remarked in the comments, your @XmlSchema lacks a namespace attribute, therefore all @XmlRootElement and @XmlType are considered to be without a namespace, unless explicitly qualified. Your java-package.info should look like this:

@XmlSchema(elementFormDefault = XmlNsForm.QUALIFIED, namespace = "http://foo/3.0/api", xmlns = {
      @XmlNs(namespaceURI = "http://foo/3.0/api", prefix = ""),
      @XmlNs(namespaceURI = "http://foo/3.0/base", prefix = "base"),
      @XmlNs(namespaceURI = "http://foo/1.0/common", prefix = "common")})
package exp._3_0.api;

import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;

The @XmlNs annotation does not specify the default namespace used in the Java package. They only provide a suggestion to the JAXB implementation on what prefixes to assign to those namespaces. In your example the tags <TokenRequest>, <software>, <softwareId> and <softwareName> do not belong to any namespace, therefore JAXB uses an empty prefix for these elements and must use another prefix (ns4) for the elements in the http://foo/3.0/api namespace (there aren't any).