Пользовательский ответ Джерси Джексона с обобщениями в JSON и XML - PullRequest
0 голосов
/ 05 июня 2018

Я некоторое время пытался придумать способ вернуть объект пользовательского ответа с атрибутом данных, который является либо объектом, либо списком.Вот несколько упрощенных соответствующих классов.Я использую Jersey v2.27 и Jackson v2.9 +

@XmlRootElement(name = "response")
public class ResponseEnvelope<T> {
  private Metadata metadata;
  private T data;

  public ResponseEnvelope(Metadata metadata, T data) {
      this.metadata = metadata;
      this.data = data;
  }
}

@XmlRootElement
public class Person {
  private String name;
  private int age;

  public Person(int age, String name) {
      this.age = age;
      this.name = name;
  }
}

@XmlRootElement
public class ListWrapper<T> {
  private List<T> list;

  public ListWrapper(List<T> list) {this.list = list;}
}

@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public class PersonResource {
    private List<Person> getPeople() {
        return Arrays.asList(new Person(20, "Monty Python"), new Person(25, "Brian");
    }

    @GET
    public Response getAllPeople() {
       ResponseEnvelope<ListWrapper<Person> response = new ResponseEnvelope<ListWrapper<Person>>(new Metadata(), new ListWrapper(getPeople());
       return Response.status(200).entity(response).build();
    }

    @GET
    @Path("{id}")
    public Response getExampleById(@PathParam("id") String id) {
        ResponseEnvelope<Person> response = new ResponseEnvelope<Person>(new Metadata(), getPeople().get(0));
       return Response.status(200).entity(response).build();
    }

}

@Provider
@Produces(MediaType.APPLICATION_XML)
public class CustomResolver implements ContextResolver<JAXBContext> {
  private JAXBContext context;

  public CustomResolver() {
    try {
      this.context = JAXBContext.newInstance(
        ArrayList.class,
        Person.class,
        ListWrapper.class,
        ResponseEnvelope.class
      );
    }
    catch (JAXBException e) {
      throw new RuntimeException(e);
    }
  }

  @Override
  public JAXBContext getContext(Class<?> type) {
    return (type.equals(ResponseEnvelope.class)) ? context : null;
  }
}

@ApplicationPath("")
public class ServerConfig extends ResourceConfig {
  public ServerConfig() {
    this.packages(true,"com.example");
    this.register(JacksonFeature.class);
  }
}

Я пробовал вещи из нескольких уроков / вопросов, таких как здесь , здесь , здесь и т. Д. (Я много пробовал ...)

Не получается получить единый ответ между двумя форматами.Когда я пытаюсь использовать List непосредственно как T data, JAXB жалуется на отсутствие контекста для ArrayList (когда я его помещаю туда, он не выводит сами элементы).

Когда я использую класс GenericEntity непосредственно в ответе, например, с

 GenericEntity<List<Person>> list = new GenericEntity<List<Person>>(people){};
 return Response.status(200).entity(list).build();`

Вывод корректен как для json, так и для xml.Однако, если я использую это в своем поле T data, я получаю исключение JAXB, что он не может найти мой класс ресурсов (кажется, вы не можете использовать GenericEntity в универсальном классе?).

Если я использую универсальный класс ListWrapper для размещения своих коллекций, я могу создавать действительные xml и json и, используя различные аннотации (@XmlAnyElement(lax = true) или @XmlElements({@XmlElement(name = "person", type = Person.class)}), могу правильно отформатировать XML, но jsonвключает в себя дополнительное свойство ListWrapper<T> list (то есть {"data": {"list": []}} вместо {"data": []}. Я пытался использовать для этого @JsonUnwrapped и @JsonSerialize с пользовательским сериализатором, но я не могу его сгладить.

Требуемый пример ответа для уникального идентификатора человека

{
    "metadata": {
        // some content
    },
    "data": {
        "age": 20,
        "name": "Monty Python"
    }
}

и XML

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<response>
    <data>
        <age>20</age>
        <name>Monty Python</name>
    </data>
    <metadata>
        <!-- some content -->
    </metadata>
</response>

и списка людей:

{
    "metadata": {
        // some content
    },
    "data": [
       {
            "age": 20,
            "name": "Monty Python"
       },
       {
            "age": 25,
            "name": "Brian"
       }
    ]
}

и XML

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<response>
    <data>
        <person>
            <age>20</age>
            <name>Monty Python</name>
        </person>
        <person>
            <age>25</age>
            <name>Brian</name>
        </person>
    </data>
    <metadata>
        <!-- some content -->
    </metadata>
</response>

1 Ответ

0 голосов
/ 13 июня 2018

Ответ состоял в том, чтобы использовать класс ListWrapper<T> и аннотировать геттер с помощью @JsonValue вместе с @XmlAnyElement(lax = true).Они создают правильно отформатированные ответы JSON и XML.Класс должен выглядеть следующим образом

import java.util.List;

import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlRootElement;

import com.fasterxml.jackson.annotation.JsonValue;

@XmlRootElement(name = "data")
public class ListWrapper<T> {
  private List<T> list;

  public ListWrapper() {}

  public ListWrapper(List<T> list) {
    this.list = list;
  }

  /**
   * @return the list
   */
  @XmlAnyElement(lax = true)
  @JsonValue
  public List<T> getList() {
    return list;
  }

  /**
   * @param list
   *               the list to set
   */
  public void setList(List<T> list) {
    this.list = list;
  }
}
...