Подтвердить что ты не робот

Джерси: массив Json с 1 элементом сериализуется как объект

Я создаю сервер REST с помощью Джерси /Java, и я обнаружил странное поведение.

У меня есть метод на сервере, который возвращает массив объектов как Json

@GET
@Path("/files")
@Produces(MediaType.APPLICATION_JSON)
public Object getFiles() throws Exception{
    DatabaseManager db = new DatabaseManager();
    FileInfo[] result = db.getFiles();
    return result;
}

Код выполняется правильно и данные возвращаются клиенту (jQuery ajax-вызов). Проблема в том, что формат возвращаемых данных изменяется, если массив "result" имеет один элемент или более одного.

Ответ с одним элементом:

{"fileInfo":{"fileName":"weather.arff","id":"10"}}

Ответ с двумя элементами:

{"fileInfo":[{"fileName":"weather.arff","id":"10"},{"fileName":"supermarket.arff","id":"11"}]}

Как вы можете видеть, в первом сценарии значение свойства "fileInfo" возвращаемого объекта является объектом, а во втором случае это массив. Что я делаю не так? Не должен ли первый случай вернуть что-то вроде этого:

{"fileInfo":[{"fileName":"weather.arff","id":"10"}]}

то есть. массив с одним объектом внутри?

Я знаю, что я могу обнаружить это на стороне клиента, но это кажется очень уродливым взломом.

Спасибо за ваше время.

4b9b3361

Ответ 1

Я закончил использовать Джексона, также описанный в официальной документации Джерси (http://jersey.java.net/nonav/documentation/latest/user-guide.html#json.pojo.approach.section).

Я пробовал это раньше, но он не работал, потому что у меня не было джексон-бана в пути построения моего проекта (на основе документации, которую я думал, что она была встроена в базовую библиотеку джерси).

Я просто добавил файл jackson-all.jar(http://wiki.fasterxml.com/JacksonDownload) и включил поддержку POJO в конфигурации

    <init-param>
          <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
          <param-value>true</param-value>
    </init-param>

И воля!

Ответ 2

Если вы использовали JAXB для создания результата JSON, вы можете настроить процессор JSON для получения более важного формата JSON.

официальный документ jersey содержит подробную конфигурацию:

Чтобы добиться более значительных изменений формата JSON, вам необходимо настроить сам механизм JSON. В экземпляре JSONConfiguration могут быть установлены различные параметры конфигурации. Затем экземпляр может быть затем использован для создания JSONConfigained JSONJAXBContext, который служит в качестве основной точки конфигурации в этой области. Чтобы передать специализированный JSONJAXBContext в Джерси, вам, наконец, нужно будет реализовать JAXBContext ContextResolver:

    @Provider
public class JAXBContextResolver implements ContextResolver<JAXBContext> {
    private final JAXBContext context;
    private final Set<Class> types;
    private Class[] ctypes = { FileInfo.class}; //your pojo class
    public JAXBContextResolver() throws Exception {
        this.types = new HashSet(Arrays.asList(ctypes));
        this.context = new JSONJAXBContext(JSONConfiguration.natural().build(),
                ctypes); //json configuration
    }

    @Override
    public JAXBContext getContext(Class<?> objectType) {
        return (types.contains(objectType)) ? context : null;
    }
}

Ответ 4

Я использую cxf, вот мой applicationContext.xml для принудительного использования массива в JSON:

<jaxrs:server id="myService" serviceName="MyService"
address="/mysvc">
<jaxrs:serviceBeans>
    <ref bean="myServiceImpl"/>
</jaxrs:serviceBeans>
<jaxrs:providers>
    <bean class="org.apache.cxf.jaxrs.provider.json.JSONProvider">
   <property name="dropRootElement" value="true" />
   <property name="supportUnwrapped" value="true" />
   <property name="namespaceMap">
      <map>
        <entry key="http://example.com/myservice" value=""/>
      </map>
   </property>
   <property name="arrayKeys">
      <list>
    <value>fileInfo</value>
      </list>
   </property>                          
    </bean>
</jaxrs:providers>
</jaxrs:server>

Ответ 5

Вы можете использовать Jettison (приходящий с Джерси) и подготовить структуру, в которой вы хотели бы использовать JSONObject и JSONArray как возвращаемые значения. Они находятся в пакете org.codehaus.jettison.json of jettison-1.3.2.jar, который является транзитивной зависимостью jerysey-json

Ответ 6

Вы также можете попробовать библиотеку Genson http://code.google.com/p/genson/. Он хорошо интегрируется с трикотажем, просто бросайте банку в свой класс и все будет работать. Это не требует, чтобы вы писали дополнительный код, он должен работать так же, как и у вас, но без какого-либо странного результата.

Ответ 7

Я немного потрудился и нашел это простое решение

В вашем pom.xml:

<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-jaxrs</artifactId>
    <version>1.9.13</version>
</dependency>
<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-xc</artifactId>
    <version>1.9.13</version>
</dependency>

В вашем web.xml:

<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
    <param-name>com.sun.jersey.config.property.packages</param-name>
    <param-value>com.other-packages;org.codehaus.jackson.jaxrs</param-value>
</init-param>

Ответ 8

Преобразование массива в ArrayList будет достаточным здесь. С подобным противоречивым вопросом я столкнулся, когда мне пришлось возвращать объект Json Array вместо списка в случае одного элемента.

Там я воспользовался приведенной ниже аннотацией, чтобы получить мою работу done-

@JsonFormat (with = JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED). Ниже приведен пример класса JSON Pojo:

import java.util.List;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown = true)
public class TAResponseMapper {

    @JsonProperty("Response")
    @JsonFormat(with = JsonFormat.Feature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED) 
    private List<TAResponse> responses;

    public List<TAResponse> getResponses() {
        return responses;
    }

    public void setResponses(List<TAResponse> responses) {
        this.responses = responses;
    }

}