¿Cómo anotar una lista usando @XmlElement?


Tengo la siguiente anotación usando javax.xml.bind.annotation.XmlElement:

@XmlElement         
public List<String> getKeywords() {
    return keywords;
}

Que produce el siguiente XML cuando marshall algún contenido de ejemplo:

<keywords>keyword1</keywords>
<keywords>keyword2</keywords>

Me gustaría obtener el siguiente XML:

<keywords>
    <keyword>keyword1</keyword>
    <keyword>keyword2</keyword>
</keywords>

, ¿Qué tipo de anotación debo usar?

He intentado

@XmlElementWrapper
@XmlElement(name="keyword")

Pero entonces todo el contenido desaparece y el resultado es:{[14]]}

<keywords/>

Lo mismo sucede también si solo intento cambiar el nombre del elemento:

@XmlElement(name="keyword")

¿Qué estoy haciendo mal?

ACTUALIZACIÓN:

Aquí está el código completo actualizado para la clase de acuerdo con las primeras respuestas, pero todavía no funciona (el resultado es una lista vacía <keywords/> cuando se marshalled a XML):

import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Content {

    private List<String> keywords;

    public Content() {}

    @XmlElementWrapper(name="keywords")
    @XmlElement(name="keyword")
    public List<String> getKeywords() {
        return keywords;
    }

    public void setKeywords(List<String> keywords) {
        this.keywords = keywords;
    }  

}

También probé lo siguiente con el mismo resultado:{[14]]}

import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Content {

    @XmlElementWrapper(name="keywords")
    @XmlElement(name="keyword")
    private List<String> keywords;

    public Content() {}

    public List<String> getKeywords() {
        return keywords;
    }

    public void setKeywords(List<String> keywords) {
        this.keywords = keywords;
    }  

}

Sin embargo, las palabras clave no están vacías, ya que lo siguiente produce <keywords>keyword1</keywords><keywords>keyword2</keywords> en lugar de una lista vacía:

import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Content {

    private List<String> keywords;

    public Content() {}

    @XmlElement
    public List<String> getKeywords() {
        return keywords;
    }

    public void setKeywords(List<String> keywords) {
        this.keywords = keywords;
    }  

}

El código para marshalling es (JAX-RS):

import java.io.StringWriter;
import javax.ws.rs.Consumes;
import javax.ws.rs.Path;
import javax.ws.rs.POST;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

@Path("process")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_XML)
public class ContentHandler {

    @POST
    public Response process(Content content) {

        StringWriter stringWriter = new StringWriter();
        try {
            JAXBContext jaxbContext = JAXBContext.newInstance(Content.class);
            Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
            jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            jaxbMarshaller.marshal(content, stringWriter);
        } catch (JAXBException e) {
            return Response.serverError().entity(e.getMessage()).build();
        }
        return Response.ok(stringWriter.toString(), MediaType.APPLICATION_XML).build();       
    }

}
Author: Adam Arold, 2014-06-19

3 answers

Necesitas aprovechar @XmlElementWrapper y @XmlElement.

Modelo Java

Contenido

import java.util.List;
import javax.xml.bind.annotation.*;

@XmlRootElement
public class Content {

    private List<String> keywords;

    public Content() {}

    @XmlElementWrapper
    @XmlElement(name="keyword")
    public List<String> getKeywords() {
        return keywords;
    }

    public void setKeywords(List<String> keywords) {
        this.keywords = keywords;
    }  

}

Código de demostración

Demo

import java.util.*;
import javax.xml.bind.*;

public class Demo {

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

        List<String> strings = new ArrayList<String>(2);
        strings.add("foo");
        strings.add("bar");

        Content content = new Content();
        content.setKeywords(strings);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(content, System.out);
    }

}

Salida

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<content>
    <keywords>
        <keyword>foo</keyword>
        <keyword>bar</keyword>
    </keywords>
</content>

Para Más Información

A continuación hay enlaces a un par de artículos de mi blog que proporcionan adicional información:

 43
Author: Blaise Doughan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-19 14:04:07

Utilice este formulario:

@XmlElementWrapper(name="keywords")
@XmlElement(name="keyword")

Tenga en cuenta que si keywords está vacío entonces obtendrá <keywords />.

A veces necesitará agregar @XmlRootElement a su clase (depende del contexto) y la anotación @XmlAccessorType(XmlAccessType.?). Normalmente uso @XmlAccessorType(XmlAccessType.FIELD) y anoto mis campos con @XmlElement.

 13
Author: Adam Arold,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-19 12:54:09

La respuesta anterior de - Blaise Doughan es completamente correcta

Otra forma sencilla es, incluso si no escribes el - @XmlElementWrapper

 private List<String> keywords;

 @XmlElementWrapper
    @XmlElement(name="keyword")
    public List<String> getKeywords() {
        return keywords;
    }

Puede usarlo de esta manera: escriba el XmlAccessorType a nivel de clase, luego el nombre del elemento XML será el mismo que el nombre del miembro de la clase-keywords

import java.util.List;
import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Content {

    private List<String> keywords;

    public Content() {}


    public List<String> getKeywords() {
        return keywords;
    }

    public void setKeywords(List<String> keywords) {
        this.keywords = keywords;
    }  

}
 0
Author: Ashish Shetkar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-06 15:25:13