Skip to main content

Namespace Prefixes and XmlElementRef

4 replies [Last post]
dharland
Offline
Joined: 2006-10-17
Points: 0

(At the end of this note is an over-simplified example that illustrates the behavior i'm seeing, just in case my prose isn't very clear.)

In my real app i have many different classes that can be root elements of XML docs. These are annotated w/ @XmlRootElement(namespace="blah"), where "blah" is always the same namespace. Often these root elements are NOT the roots of XML docs, but are instead contained in other elements. In addition, some of these container classes contain collections of interface or abstract classes. By using the XmlElementRef, i've been able to handle polymorphism among the contained classes.

When a container class (such as "ParkingLot", below) holds a collection of concrete classes, and i use @XmlElement, the contained elements (such as "LampPost", below) are not prefixed with the namespace. This is fine.

However, when a container class holds a collection of non-concrete classes, and i use @XmlElementRef, the contained elements use a namespace prefix. If i don't use a schema file, all is well. However, when i do, the marshaller (in my case com.sun.xml.bind.v2.runtime.MarshallerImpl) throws a SAXParseException saying it found invalid content "ns2:foo" when it expected "foo".

I'm not sure if the marshaller should accept what looks to me to be sensible output, or if the namespace prefix should not be generated to begin with, or both. It does seem odd that using @XmlElement gives no prefix and @XmlElementRef does, and when all else seems equal.

Here's an example:

<br />
package namespaceTrouble;</p>
<p>import java.util.*;<br />
import javax.xml.bind.*;<br />
import javax.xml.bind.annotation.*;</p>
<p>/**<br />
 * Demonstrate trouble with namespaces when using XmlElementRefs.<br />
 * Not sure if JAXB is OK and parser is wrong, or vice versa.<br />
 * This pgm does not show parser's trouble, which is with<br />
 * an inner element having the "ns2:" prefix.<br />
 */<br />
@XmlRootElement(namespace="myNameSpace")<br />
public class ParkingLot<br />
{<br />
  @XmlElementWrapper<br />
  @XmlElement(name="lampPost")<br />
  List   lights;</p>
<p>  @XmlElementWrapper<br />
  @XmlElementRefs<br />
  (<br />
    {<br />
      @XmlElementRef(type=Truck.class),<br />
      @XmlElementRef(type=Motorcycle.class)<br />
    }<br />
  )<br />
  List vehicles;</p>
<p>  public ParkingLot()<br />
  {<br />
    lights   = new ArrayList();<br />
    vehicles = new ArrayList();</p>
<p>    for (int x=0; x < 5; x++)<br />
      lights.add(new LampPost());</p>
<p>    for (int x=0; x < 10; x++)<br />
      vehicles.add(Math.random() < 0.5 ? new Truck() : new Motorcycle());<br />
  }</p>
<p>  public static void main(String[] args)<br />
  {<br />
    ParkingLot myLot = new ParkingLot();</p>
<p>    try<br />
    {<br />
      JAXBContext jaxb = JAXBContext.newInstance(ParkingLot.class);<br />
      Marshaller xmlConverter = jaxb.createMarshaller();</p>
<p>      xmlConverter.setProperty("jaxb.formatted.output", true);<br />
      xmlConverter.marshal(myLot, System.out);<br />
    }<br />
    catch (Exception ex)<br />
    {<br />
      ex.printStackTrace();<br />
    }<br />
  }<br />
}<br />

<br />
package namespaceTrouble;</p>
<p>import javax.xml.bind.annotation.XmlElement;<br />
import javax.xml.bind.annotation.XmlRootElement;</p>
<p>@XmlRootElement(namespace="myNameSpace")<br />
public class LampPost<br />
{<br />
  @XmlElement private double  feetHigh;<br />
  @XmlElement private boolean broken;</p>
<p>  public LampPost()<br />
  {<br />
    feetHigh = 10.0 + Math.random() * 10.0;<br />
    broken   = Math.random() < 0.9 ? false : true;<br />
  }<br />
}<br />

<br />
package namespaceTrouble;</p>
<p>import javax.xml.bind.annotation.XmlElement;</p>
<p>public abstract class Automobile<br />
{<br />
  @XmlElement private double metersLong;</p>
<p>  public Automobile()<br />
  {<br />
    metersLong= 3.0 + 3.0 * Math.random();<br />
  }<br />
}<br />

<br />
package namespaceTrouble;</p>
<p>import javax.xml.bind.annotation.XmlElement;<br />
import javax.xml.bind.annotation.XmlRootElement;</p>
<p>@XmlRootElement(namespace="myNameSpace")<br />
public class Truck extends Automobile<br />
{<br />
  @XmlElement private int axelCount;</p>
<p>  public Truck()<br />
  {<br />
    super();<br />
    axelCount = 2 + (int)(3.0 * Math.random());<br />
  }<br />
}<br />

<br />
package namespaceTrouble;</p>
<p>import javax.xml.bind.annotation.XmlElement;<br />
import javax.xml.bind.annotation.XmlRootElement;</p>
<p>@XmlRootElement(namespace="myNameSpace")<br />
public class Motorcycle extends Automobile<br />
{<br />
  @XmlElement private int maxMph;</p>
<p>  public Motorcycle()<br />
  {<br />
    super();<br />
    maxMph = 100 + (int)(50.0 * Math.random());<br />
  }<br />
}<br />

Partial Output:

<br />
<?xml version="1.0" encoding="UTF-8" standalone="yes"?></p>
<p>            18.298334809730818<br />
            false</p>
<p>...</p>
<p>            10.39113610915307<br />
            false</p>
<p>            4.886731528560258<br />
            3</p>
<p>            4.245026532764784<br />
            109</p>
<p>...</p>
<p>            4.844087538828457<br />
            145</p>
<p>

(The space between "ns2:" and "parkingLot" above were put there by me solely for this post, as it looked like colon-'p' was being interpreted as an emoticon.)

As you can see, Motorcycle and Truck objects are prefixed with ns2 while LampPost objects are not. In my full app the marshaller complains about the leading "ns2:". If i remove the namespace="blah" parameter from the @XmlRootElement annotations on Motorcycle and Truck, i'm fine when those objects are contain in ParkingLot, but not when they're roots (because now the Marshaller correctly demands namespace information).

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
kohsuke
Offline
Joined: 2003-06-09
Points: 0

> It does seem odd that using @XmlElement gives no prefix
> and @XmlElementRef does, and when all else seems equal.

This is actually because the spec decided to "align" the default to the similar concept in XML Schema (which is widely considered broken.) This is one of the decisions I pesonally regret, but the EG has spoken and the decision was made conciouslly.

I think what you really want to do is to put

@XmlSchema(namespace="blah",elementFormDefault=QUALIFIED)

on the packages that includes your XML-bound classes. You no longer need @XmlRootElement.namespace() once you have this.

dharland
Offline
Joined: 2006-10-17
Points: 0

Kohsuke,

Thanks a bunch! You really seem to have a knack for answering these posts.

It'll be awhile before i can say for sure that i have it working in my real app, but after struggling a little bit, i did get the toy app to work. For the record, i made these changes:

+ Wrote a schema (something i hadn't done for original post, but which was a large part of my real issue). Schema is reproduced, below.

+ Modified ParkingLot to use schema.

+ All classes: removed the namespace="myNameSpace" from @XmlRootElement

+ Created a package-info.java and added this:
[code]
@javax.xml.bind.annotation.XmlSchema(
namespace="http://phony.com/parkingLot",
elementFormDefault=javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
[/code]

+ Added 'elementFormDefault = "qualified"' to the schema. (That proved to be key.)

The root element of the XML output now has default (prefix-less) namespace and none of the inner elements have prefixes. Here's a snippet:
[code]


15.643028599721717
...

3.5845338732891596
3

[/code]
The schema:
[code]

version = "1.0"
xmlns: xs = "http://www.w3.org/2001/XMLSchema"
targetNamespace = "http://phony.com/parkingLot"
xmlns:PL = "http://phony.com/parkingLot"
elementFormDefault = "qualified">







































[/code]
My real app has XML attributes, whereas this toy app does not. I hope i don't run into any trouble there ;).

Thanks again for the help.

kohsuke
Offline
Joined: 2003-06-09
Points: 0

Thanks for sharing what you found. If everyone would do that, the archive of this forum would be a lot more useful!

baskar_sks
Offline
Joined: 2008-07-22
Points: 0

Hi All,
I have some doubt in JAXB unmarshalling.

My one of the java file content is,

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"content"
})
@XmlRootElement(name = "RedlinedBOMRowCurrent")
public class RedlinedBOMRowCurrent {

@XmlElementRefs({
@XmlElementRef(name = "ItemRev", namespace = "http://support.agile.com/misc/axml/2006/03/", type = JAXBElement.class),
@XmlElementRef(name = "ItemDescription", namespace = "http://support.agile.com/misc/axml/2006/03/", type = JAXBElement.class),
@XmlElementRef(name = "AttachmentsImage", namespace = "http://support.agile.com/misc/axml/2006/03/", type = JAXBElement.class),
@XmlElementRef(name = "HasBeenRedlinedImage", namespace = "http://support.agile.com/misc/axml/2006/03/", type = JAXBElement.class),
@XmlElementRef(name = "ItemNumber", namespace = "http://support.agile.com/misc/axml/2006/03/", type = JAXBElement.class),
@XmlElementRef(name = "ItemList03", namespace = "http://support.agile.com/misc/axml/2006/03/", type = JAXBElement.class),
@XmlElementRef(name = "Qty", namespace = "http://support.agile.com/misc/axml/2006/03/", type = JAXBElement.class),
@XmlElementRef(name = "FindNum", namespace = "http://support.agile.com/misc/axml/2006/03/", type = JAXBElement.class),
@XmlElementRef(name = "ReferenceDesignators", namespace = "http://support.agile.com/misc/axml/2006/03/", type = ReferenceDesignators.class),
@XmlElementRef(name = "ItemList25", namespace = "http://support.agile.com/misc/axml/2006/03/", type = JAXBElement.class),
@XmlElementRef(name = "PendingChangesImage", namespace = "http://support.agile.com/misc/axml/2006/03/", type = JAXBElement.class),
@XmlElementRef(name = "ItemLifecyclePhase", namespace = "http://support.agile.com/misc/axml/2006/03/", type = JAXBElement.class),
@XmlElementRef(name = "ManufacturerImage", namespace = "http://support.agile.com/misc/axml/2006/03/", type = JAXBElement.class)
})
protected List

content; /** * Gets the rest of the content model. * *

* You are getting this "catch-all" property because of the following reason: * The field name "ReferenceDesignators" is used by two different parts of a schema. See: * line 130 of file:/C:/Baskar/MyEclipse/FirstBallyProject/AgileToMapics/agile.xsd * line 127 of file:/C:/Baskar/MyEclipse/FirstBallyProject/AgileToMapics/agile.xsd *

* To get rid of this property, apply a property customization to one * of both of the following declarations to change their names: * Gets the value of the content property. * *

* This accessor method returns a reference to the live list, * not a snapshot. Therefore any modification you make to the * returned list will be present inside the JAXB object. * This is why there is not a set method for the content property. * *

* For example, to add a new item, do as follows: *

     *    getContent().add(newItem);
     * 
* * *

* Objects of the following type(s) are allowed in the list * {@link JAXBElement }{@code <}{@link String }{@code >} * {@link JAXBElement }{@code <}{@link String }{@code >} * {@link JAXBElement }{@code <}{@link Boolean }{@code >} * {@link JAXBElement }{@code <}{@link Boolean }{@code >} * {@link JAXBElement }{@code <}{@link String }{@code >} * {@link JAXBElement }{@code <}{@link String }{@code >} * {@link JAXBElement }{@code <}{@link Byte }{@code >} * {@link JAXBElement }{@code <}{@link Byte }{@code >} * {@link ReferenceDesignators } * {@link JAXBElement }{@code <}{@link String }{@code >} * {@link JAXBElement }{@code <}{@link Boolean }{@code >} * {@link JAXBElement }{@code <}{@link String }{@code >} * {@link JAXBElement }{@code <}{@link Boolean }{@code >} * * */ public List getContent() { if (content == null) { content = new ArrayList(); } return this.content; } } I need help to unmarshal this. When i say RedlinedBOMRowCurrent.getContent() i get object list. I don't know how to get each XMLElementRef values from this object list. Thanks, Baskar.S