4

I am facing the following problems with JAXB: It looks like JAXB is analysing properties from the most deep child class to parent, and child property has a priority. I would like to change somehow this behaviour. In particular:

The child class:

package test.sub;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlTransient;


@XmlAccessorType(XmlAccessType.NONE)
public class BasicDocument {

    private String comment;

    public String getComment() {
        return comment;
    }

    public void setComment(String cost) {
        this.comment = cost;
    }
}

The parent class:

package test;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

import test.sub.BasicDocument;

@XmlRootElement(name="Description", namespace="http://www.w3.org/1999/02/22-rdf-syntax-ns#")
@XmlAccessorType(XmlAccessType.PROPERTY)
class Document extends BasicDocument {

    private String identifier;

    @XmlElement(name = "identifier", namespace = "http://purl.org/dc/terms/")
    public String getIdentifier() {
        return identifier;
    }

    public void setIdentifier(String identifier) {
        this.identifier = identifier;
    }

    @Override
    @XmlElement(name = "abstract", namespace = "http://purl.org/dc/terms/")
    public String getComment() {
        return super.getComment();
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
    }
}

Marshalling works fine:

Document document = new Document();

document.setIdentifier("12A");
document.setComment("special");

StringWriter w = new StringWriter();

jaxbContext.createMarshaller().marshal(document, new StreamResult(w));

System.out.println(w);

Output:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:Description xmlns:ns2="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://purl.org/dc/terms/">
    <abstract>special</abstract>
    <identifier>12A</identifier>
</ns2:Description>

But marshalling ignores the property which is in child BasicDocument class (t.xml is exactly the XML above):

JAXBContext jaxbContext = JAXBContext.newInstance(Document.class);

Document document = (Document) jaxbContext.createUnmarshaller().unmarshal(Document.class.getResourceAsStream("t.xml"));

System.out.println("out: " + document);

Output:

out: Document[identifier=12A,comment=<null>]

Expected:

out: Document[identifier=12A,comment=special]

Basically @XmlAccessorType(XmlAccessType.NONE) on BasicDocument (see Ignore a parent class when Serializing to XML) had no effect. Also creating package-info.java in package test.sub (see @XmlTransient on third-party or external super class) like this:

@XmlAccessorType(XmlAccessType.NONE)
package test.sub;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;

had no effect. Only @XmlTransient public class BasicDocument worked. Ideally I would like not to add any annotation on child and control this behaviour only via package-info.java. How can I do this?

Tested on JDK 1.6.0_27, and additionally with JAXB 2.2.4-1 runtime in classpath.

Is it feature or bug?

Community
  • 1
  • 1
dma_k
  • 10,431
  • 16
  • 76
  • 128

1 Answers1

2

You just need to add the setComment method to the Document class. Without it, it is being treated as a write-only property even though the method exists on a parent class.

public void setComment(String comment) {
    super.setComment(comment);
}

Full Source for Document

package test;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

import test.sub.BasicDocument;

@XmlRootElement(name="Description", namespace="http://www.w3.org/1999/02/22-rdf-syntax-ns#")
@XmlAccessorType(XmlAccessType.PROPERTY)
class Document extends BasicDocument {

    private String identifier;

    @XmlElement(name = "identifier", namespace = "http://purl.org/dc/terms/")
    public String getIdentifier() {
        return identifier;
    }

    public void setIdentifier(String identifier) {
        this.identifier = identifier;
    }

    @Override
    @XmlElement(name = "abstract", namespace = "http://purl.org/dc/terms/")
    public String getComment() {
        return super.getComment();
    }

    public void setComment(String comment) {
        super.setComment(comment);
    }

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
    }

}
bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • Thanks for the answer. Why `@XmlTransient` had a magic cure effect in this case? – dma_k Feb 15 '12 at 22:31
  • 1
    @dma_k - `@XmlTransient` worked on the super class, because that tells the JAXB implementation to ignore it from the inheritance hierarchy and treat the properties as if they belong to the subclasses: http://blog.bdoughan.com/2011/06/ignoring-inheritance-with-xmltransient.html – bdoughan Feb 16 '12 at 15:57
  • I faced kind of "interesting behaviour" from JAXB. I have introduced an intermediate class `CommentHolder` in `BasicDocument` which holds `String comment` property. `BasicDocument` is added `CommentHolder getCommentHolder()` and `setCommentHolder(CommentHolder commentHolder)` in place of get/setComment. `Document` class was updated like following `String getComment() { return super.getCommentHolder().getComment(); }` and `void setComment(String comment) { super.getCommentHolder().setComment(comment); }` so its API is the same. JAXB now serializes `commentHolder` property. Is it normal? Thanks – dma_k Feb 28 '12 at 11:14