Raul Leaño Martinet
Raul Leaño Martinet

Reputation: 2113

How to change the name of the XmlRootElement in JAXB deppending on the object?

I have the following objects

Film

@Entity
@Table(name="film")
@XmlRootElement(name = "film")
public class Film implements Serializable {

    @Id
    @Column(name="id")
    private String fbId;

    @Column(name="title")
    private String title;

    @ManyToMany
    @JoinTable(
        name="direction",
        joinColumns={@JoinColumn(name="film", referencedColumnName="id")},
        inverseJoinColumns={@JoinColumn(name="person", referencedColumnName="id")})
    private Collection<Person> directors;

    @OneToMany(cascade={CascadeType.ALL}, mappedBy="film")
    @MapKey(name="character")
    private Map<String, Performance> performances;

    //GETTERS
    @XmlAttribute(name = "fbId")
    public String getFreebaseId() {
        return this.fbId;
    }

    @XmlElementRefs({
        @XmlElementRef(name="director", type=Person.class)
    })
    public Collection<Person> getDirectors() {
        return this.directors;
    }

    @XmlAnyElement(lax=true)
    public Collection<Performance> getPerformances() {
        ArrayList performancesArray = new ArrayList<Performance>();
        for (Map.Entry<String, Performance> entry : this.performances.entrySet()) {
            Performance value = entry.getValue();
            performancesArray.add(value);
        }
        return performancesArray;
    }

}

Person

@Entity
@Table(name="person")
public class Person implements Serializable {

    @Id
    @Column(name="id")
    private String fbId;

    @Column(name="first_name")
    private String firstName;

    @Column(name="last_name")
    private String lastName;

    @OneToMany(cascade={CascadeType.ALL}, mappedBy="actor")
    @XmlTransient
    private Collection<Performance> performances;

    //GETTERS
    @XmlAttribute(name = "fbId")
    public String getFreebaseId() {
        return this.fbId;
    }

    @XmlElement(name = "firstName")
    public String getFirstName() {
        return this.firstName;
    }

    @XmlElement(name = "lastName")
    public String getLastName() {
        return this.lastName;
    }

    @XmlTransient
    public Collection<Performance> getPerformances() {
        return this.performances;
    }

}

Each film has one (or many) directors and performances which are "Persons". I don't need a class for directors but I do for perfomances because they have more info.

Performance

@Entity
@Table(name="performance")
@XmlRootElement(name = "performace")
public class Performance implements Serializable {

    @Id
    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="film")
    private Film film;

    @Id
    @Column(name="film_character")
    private String character;

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="actor")
    private Person actor;

    //TO STRING
    public String toString() {
        return this.actor.toString() + " - " + this.character;
    }

    //GETTERS
    @XmlTransient
    public Film getFilm() {
        return this.film;
    }

    @XmlElementRefs({
        @XmlElementRef(name = "firstName", type = Person.class),
        @XmlElementRef(name = "lastName", type = Person.class)
    })
    public Person getActor() {
        return this.actor;
    }

    @XmlElement(name = "character")
    public String getCharacter() {
        return this.character;
    }

}

When I run this through JAXB I get this:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<film fbId="1">
    <description>Nice film</description>
    <person fbId="1">
        <firstName>Quentin</firstName>
        <lastName>Tarantino</lastName>
    </person>
    <performace>
        <person fbId="4">
            <firstName>Steve</firstName>
            <lastName>Buscemi</lastName>
        </person>
        <character>Billy</character>
    </performace>
    <performace>
        <person fbId="2">
            <firstName>Jhon</firstName>
            <lastName>Travolta</lastName>
        </person>
        <character>Vincent</character>
    </performace>
    <performace>
        <person fbId="3">
            <firstName>Samuel</firstName>
            <lastName>L Jackson</lastName>
        </person>
        <character>Jules</character>
    </performace>
    <performace>
        <person fbId="1">
            <firstName>Quentin</firstName>
            <lastName>Tarantino</lastName>
        </person>
        <character>Jimmie</character>
    </performace>
    <title>Pulp Fiction</title>
</film>

Is there a way to change the name of the "person"? To get something like this:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<film fbId="1">
    <description>Nice film</description>
    <director fbId="1">
        <firstName>Quentin</firstName>
        <lastName>Tarantino</lastName>
    </director>
    <performace fbId="4">
        <firstName>Steve</firstName>
        <lastName>Buscemi</lastName>
        <character>Billy</character>
    </performace>
    <performace fbId="2">
        <firstName>Jhon</firstName>
        <lastName>Travolta</lastName>
        <character>Vincent</character>
    </performace>
    <performace fbId="3">
        <firstName>Samuel</firstName>
        <lastName>L Jackson</lastName>
        <character>Jules</character>
    </performace>
    <performace fbId="1">
        <firstName>Quentin</firstName>
        <lastName>Tarantino</lastName>
        <character>Jimmie</character>
    </performace>
    <title>Pulp Fiction</title>
</film>

Upvotes: 4

Views: 3994

Answers (1)

lexicore
lexicore

Reputation: 43651

Options:

  • Make Director extends Person with its own @XmlRootElement
  • Use JAXBElement<? extends Person> instead of Person

The problem is, @XmlElementRef.name does not work for @XmlRootElement, read here:

If type() is JAXBElement.class , then namespace() and name() point to a factory method with XmlElementDecl. The XML element name is the element name from the factory method's XmlElementDecl annotation or if an element from its substitution group (of which it is a head element) has been substituted in the XML document, then the element name is from the XmlElementDecl on the substituted element.

If type() is not JAXBElement.class, then the XML element name is the XML element name statically associated with the type using the annotation XmlRootElement on the type. If the type is not annotated with an XmlElementDecl, then it is an error.

If type() is not JAXBElement.class, then this value must be "".

By the way

@XmlElementRefs({
    @XmlElementRef(name = "firstName", type = Person.class),
    @XmlElementRef(name = "lastName", type = Person.class)
})

does not seem valid to me. @XmlElementRef are not supposed to map properties of the target class.

Upvotes: 2

Related Questions