简体   繁体   中英

Unmarshall xml list of objects to java list without class list

With JAXB, I would like to unmarshall an xml document that contains several serialized objects like this :

<?xml version="1.0" encoding="UTF-8"?>
<Users>
<User>
    <firstName>first name value 1</firstName>
    <lastName>last name value 1</lastName>
    <account>
        <expiration>expire 1</expiration>
        <login>login 1</login>
    </account>
</User>
<User>
    <firstName>first name value 2</firstName>
    <lastName>last name value 2</lastName>
    <account>
        <expiration>expire 2</expiration>
        <login>login 2</login>
    </account>
</User>
...
</Users>

The fact is that I don't want to create a new class named "Users" for instance and which contains a list of User's elements (with @XmlWrapper annotation).

Here is my java object that I have to translate to xml :

@Entity
@Table(name="USERS")
@XmlRootElement(name="User")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String firstName;
    private String lastName;

    @OneToOne(cascade={CascadeType.REMOVE}, mappedBy="user")
    private Account account;

    @XmlAttribute
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @XmlElement
    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    @XmlElement
    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    @XmlInverseReference(mappedBy="user")
    @XmlElement
    public Account getAccount() {
        return account;
    }

    public void setAccount(Account account) {
        this.account = account;
    }
}

And for the moment I can juste unmarshall just one xml to an User java object. Like this :

@Test
    public void test2() {
        try {
            JAXBContext jc = JAXBContext.newInstance(User.class);
            Unmarshaller u = jc.createUnmarshaller();

            File f = new File("user.xml");
            User element = (User) u.unmarshal(f);

            System.out.println(
                    element.getAccount().getLogin()
                    );

        } catch (JAXBException e) {
            e.printStackTrace();
        }
    }

And I would like to get a User java list instances instead of just a User instance. Like this for instance :

List<User> elements = (List<User>) u.unmarshal(f);

I hope this is possible and I would like to know how ;)


Thanks a lot for your reply Blaise.

I tried to make as you did but I've got error :

java.lang.ClassCastException: com.sun.org.apache.xerces.internal.dom.ElementNSImpl cannot be cast to com.thales.momoko.ws.model.User

Here are some relevant part of my code :

public class Tools<T> {

public List<T> getItems(Class<T> entityClass, String xmlLocation) {
    try {
        JAXBContext jc = JAXBContext.newInstance(Wrapper.class, entityClass.getClass());
        Unmarshaller unmarshaller = jc.createUnmarshaller();

        BufferedReader br = new BufferedReader(
                new InputStreamReader(
                this.getClass().getClassLoader().getResourceAsStream(xmlLocation)));

        System.out.println(br.readLine());

        Wrapper<T> wrapper = (Wrapper<T>) unmarshaller.unmarshal(new StreamSource(br), Wrapper.class).getValue();

        System.out.println(wrapper);

        return wrapper.getItems();

    } catch (JAXBException ex) {
        Logger.getLogger(Tools.class.getName()).log(Level.SEVERE, null, ex);
    } catch (IOException ex) {
        Logger.getLogger(Tools.class.getName()).log(Level.SEVERE, null, ex);
    }
    return null;
}
}

The first println works fine because it displays the first line of the xml file :

<?xml version=....>

The second println shows a problem with the unmarshalling :

Wrapper{items=[[user: null], [user: null], [user: null], [user: null], [user: null], [user: null], [user: null], [user: null], [user: null], [user: null], [user: null], [user: null], [user: null], [user: null], [user: null]]}

The wrapper :

public class Wrapper<T> {

private List<T> items = new ArrayList<>();

@XmlAnyElement(lax=true)
public List<T> getItems() {
    return items;
}

@Override
public String toString() {
    return "Wrapper{" + "items=" + items + '}';
}
}

And finally the call of the unmarshaller :

@PostConstruct
public void init() {
   this.entityClass = User.class;
    for (User user : (List<User>) new Tools<User>().getItems(User.class, "user.xml"))
        System.out.println(user.getFirstName());
}

It gives me an error at the line with the "for" instruction.

Have you got an idea about this error ?

Thanks another time !

EDIT

Solution :

public class Tools<T> {

public static <T> List<T> getItems(Class<T> entityClass, String xmlLocation) {
    try {
        JAXBContext jc;
        synchronized (JAXBContext.class) {
            jc = JAXBContext.newInstance(Wrapper.class, entityClass);
        }

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        BufferedReader br = new BufferedReader(
                new InputStreamReader(
                Import.class.getClassLoader().getResourceAsStream(xmlLocation)));

        Wrapper<T> wrapper = (Wrapper<T>) unmarshaller.unmarshal(new StreamSource(br), Wrapper.class).getValue();

        return wrapper.getItems();

    } catch (JAXBException ex) {
        Logger.getLogger(Import.class.getName()).log(Level.SEVERE, null, ex);
    }
    return null;
}
}

The call :

@PostConstruct
public void init() {
   this.entityClass = User.class;
    for (User user : (List<User>) Tools.getItems(User.class, "user.xml"))
        em.persist(user);
}

You could use JAXB with StAX to do the following:

import java.util.*;
import javax.xml.bind.*;
import javax.xml.stream.*;
import javax.xml.transform.stream.StreamSource;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(User.class);

        XMLInputFactory xif = XMLInputFactory.newFactory();
        StreamSource xml = new StreamSource("src/forum17047306/input.xml");
        XMLStreamReader xsr = xif.createXMLStreamReader(xml);

        List<User> users = new ArrayList<User>();
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        while(xsr.getEventType() != XMLStreamReader.END_DOCUMENT) {
            if(xsr.isStartElement() && "User".equals(xsr.getLocalName())) {
                User user = (User) unmarshaller.unmarshal(xsr);
                users.add(user);
            }
            xsr.next();
        }
        System.out.println(users.size());
    }

}

UPDATE

You may prefer the following approach for handling lists using a generic list wrapper object:

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM