в Java, когда я использую
@Produces("application/json")
аннотация, выход не формируется в читаемую человеком форму. Как это сделать?
в Java, когда я использую
@Produces("application/json")
аннотация, выход не формируется в читаемую человеком форму. Как это сделать?
Для справки: если вы хотите включить симпатичный вывод только для некоторых ресурсов, вы можете использовать аннотацию @JacksonFeatures для метода ресурса.
Вот пример:
@Produces(MediaType.APPLICATION_JSON)
@JacksonFeatures(serializationEnable = { SerializationFeature.INDENT_OUTPUT })
public Bean resource() {
return new Bean();
}
Создайте этот класс в любом месте вашего проекта. Он будет загружен при развертывании. Обратите внимание на .configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
, который настраивает преобразователь для форматирования вывода.
Для Jackson 2.0 и более поздних версий замените две строки .configure()
на следующие: .configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false)
.configure(SerializationFeature.INDENT_OUTPUT, true);
И измените свой импорт соответственно.
package com.secret;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
/**
*
* @author secret
*/
@Provider
@Produces(MediaType.APPLICATION_JSON)
public class JacksonContextResolver implements ContextResolver<ObjectMapper> {
private ObjectMapper objectMapper;
public JacksonContextResolver() throws Exception {
this.objectMapper = new ObjectMapper();
this.objectMapper
.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
}
@Override
public ObjectMapper getContext(Class<?> objectType) {
return objectMapper;
}
}
Имейте в виду, что форматирование отрицательно влияет на производительность.
Вот как вы можете правильно сделать условный симпатичный/некрасивый json-вывод, основанный на наличии "симпатичного" в строке запроса.
Создайте PrettyFilter
, который реализует ContainerResponseFilter
, который будет выполняться в каждом запросе:
@Provider
public class PrettyFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext reqCtx, ContainerResponseContext respCtx) throws IOException {
UriInfo uriInfo = reqCtx.getUriInfo();
//log.info("prettyFilter: "+uriInfo.getPath());
MultivaluedMap<String, String> queryParameters = uriInfo.getQueryParameters();
if(queryParameters.containsKey("pretty")) {
ObjectWriterInjector.set(new IndentingModifier(true));
}
}
public static class IndentingModifier extends ObjectWriterModifier {
private final boolean indent;
public IndentingModifier(boolean indent) {
this.indent = indent;
}
@Override
public ObjectWriter modify(EndpointConfigBase<?> endpointConfigBase, MultivaluedMap<String, Object> multivaluedMap, Object o, ObjectWriter objectWriter, JsonGenerator jsonGenerator) throws IOException {
if(indent) jsonGenerator.useDefaultPrettyPrinter();
return objectWriter;
}
}
}
И в значительной степени это!
Вам нужно будет убедиться, что этот класс будет использоваться Джерси либо автоматическим сканированием пакетов, либо зарегистрирован вручную.
Прошло несколько часов, пытаясь добиться этого, и обнаружил, что никто не опубликовал готовое к использованию решение раньше.
Основываясь на полезном ответе DaTroop, вот еще одна версия, которая позволяет выбирать между оптимизированным json и форматированным json на основе отсутствия или наличия "симпатичного" параметра:
package test;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
@Provider
@Produces(MediaType.APPLICATION_JSON)
public class JacksonContextResolver implements ContextResolver<ObjectMapper> {
private ObjectMapper prettyPrintObjectMapper;
private UriInfo uriInfoContext;
public JacksonContextResolver(@Context UriInfo uriInfoContext) throws Exception {
this.uriInfoContext = uriInfoContext;
this.prettyPrintObjectMapper = new ObjectMapper();
this.prettyPrintObjectMapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
}
@Override
public ObjectMapper getContext(Class<?> objectType) {
try {
MultivaluedMap<String, String> queryParameters = uriInfoContext.getQueryParameters();
if(queryParameters.containsKey("pretty")) {
return prettyPrintObjectMapper;
}
} catch(Exception e) {
// protect from invalid access to uriInfoContext.getQueryParameters()
}
return null; // use default mapper
}
}
Если вы используете Spring, вы можете глобально установить свойство
spring.jackson.serialization.INDENT_OUTPUT=true
Дополнительная информация на https://docs.spring.io/spring-boot/docs/current/reference/html/howto-properties-and-configuration.html
Если вы используете зависимость jersey-media-json-binding
, в которой используются Yasson (официальный RI JSR-367) и JAVAX-JSON, вы можете ввести красивую печать следующим образом:
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
@Provider
public class RandomConfig implements ContextResolver<Jsonb> {
private final Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withFormatting(true));
public RandomConfig() { }
@Override
public Jsonb getContext(Class<?> objectType) {
return jsonb;
}
}