0

I have an XML structure that I want to unmarshal using JaxB, while maintaining the order:

<body>
    <apple>
        <!-- ... -->
    </apple>
    <banana>
        <!-- ... -->
    </banana>
    <apple>
        <!-- ... -->
    </apple>
</body>

With this snippet I get the desired output

@XmlElement(name = "apple")
List<Apple> apples;
@XmlElement(name = "banana")
List<Banana> bananas;

// getters & setter

I get the desired output, but in the "wrong" order. So if I iterate the lists, I would get

1. Apple
2. Apple
3. Banana

In the XML above, the order was:

1. Apple
2. Banana
3. Apple

My idea was to make an abstract class (or interface) Fruit, so that Apple and Banana can extend from it:

abstract class Fruit{}

class Apple extends Fruit{
    //...
}
class Banana extends Fruit{
    //...
}

Then unmarshal the elements Apple and Banana into a (Linked-)List of Fruits, as shown here:

List<Fruit> fruits;

@XmlElements({
    @XmlElement(name = "apple", type = Apple.class),
    @XmlElement(name = "banana", type = Banana.class)
})

@XmlElementWrapper
public List<Fruit> getFruits() {
    return fruits;
}

Unfortunately, fruits stays null.

Evgenij Reznik
  • 17,916
  • 39
  • 104
  • 181

1 Answers1

0

It worked for me without @XmlElementWrapper annotation:

@XmlElements({
    @XmlElement(name = "apple", type = Apple.class),
    @XmlElement(name = "banana", type = Banana.class)
})
public List<Fruit> getFruits() {
    return fruits;
}

I suppose @XmlElementWrapper expects one more element fruits inside the body element.

Update (full code):

public class Main {

    public static void main(String[] args) throws Exception {

        JAXBContext ctx = JAXBContext.newInstance(Body.class);
        Body b = (Body)ctx.createUnmarshaller().unmarshal(new StreamSource(new StringReader(xml)));
        System.out.println(b.getFruits());
    }

    static String xml = "<body>\n"
            + "    <apple>\n"
            + "        <!-- ... -->\n"
            + "    </apple>\n"
            + "    <banana>\n"
            + "        <!-- ... -->\n"
            + "    </banana>\n"
            + "    <apple>\n"
            + "        <!-- ... -->\n"
            + "    </apple>\n"
            + "</body>";
}

@XmlRootElement(name = "body")
public class Body {

    private List<Fruit> fruits;

    @XmlElements({
        @XmlElement(name = "apple", type = Apple.class),
        @XmlElement(name = "banana", type = Banana.class)
    })
    public List<Fruit> getFruits() {
        return fruits;
    }

    public void setFruits(List<Fruit> fruits) {
        this.fruits = fruits;
    }

}

public abstract class Fruit {
    
}

public class Apple extends Fruit {
    
}

public class Banana extends Fruit {
    
}

Output of main() method:

[org.kdv.so.models.Apple@530612ba, org.kdv.so.models.Banana@2a40cd94, org.kdv.so.models.Apple@f4168b8]

Dependencies:

<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.1</version>
    <type>jar</type>
</dependency>
<dependency>
    <groupId>org.glassfish.jaxb</groupId>
    <artifactId>jaxb-runtime</artifactId>
    <version>2.3.1</version>
</dependency>
Alexandra Dudkina
  • 4,302
  • 3
  • 15
  • 27
  • Without `@XmlElementWrapper` I get a `NullPointerException`. Without any annotations JaxB doesn't know, that `fruits` exists. Do you know, how to specify, that it should put the elements `apple` and `banana` into the new object `fruits`? – Evgenij Reznik Oct 20 '20 at 19:16
  • Could you please post your code? I can't even run my program when I remove `@XmlElementWrapper` – Evgenij Reznik Oct 21 '20 at 16:14