Джерси: массив 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"}]}
то есть. массив с одним объектом внутри?
Я знаю, что я могу обнаружить это на стороне клиента, но это кажется очень уродливым взломом.
Спасибо за ваше время.
Ответы
Ответ 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;
}
}
Ответ 3
Также рассмотрим следующий ответ, который решил это для меня:
Как настроить сериализацию списка объектов JAXB в JSON?
Ответ 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;
}
}