Spring MVCを使用してRESTサービスを開発している間、開発ではJSONを「きれいに印刷」し、本番では通常(空白を減らして)レンダリングしたいと思います。
この質問を投稿したときに回答がありましたが、より良い代替ソリューションがある場合に備えて、とにかく投稿したいと思いました。ここに私の経験がありました:
最初のものが最初です。 MappingJacksonHttpMessageConverter
は、Jackson ObjectMapper
インスタンスを挿入し、そのインスタンスで(Springクラス経由ではなく)Jackson設定を実行することを期待しています。
これを行うのと同じくらい簡単だと思いました:
ObjectMapperFactoryBean
に挿入できるObjectMapper
インスタンスをカスタマイズできるMappingJacksonHttpMessageConverter
実装を作成します。例えば:
_<bean id="jacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper">
<bean class="com.foo.my.ObjectMapperFactoryBean">
<property name="prettyPrint" value="${json.prettyPrint}"/>
</bean>
</property>
</bean>
_
そして、私のObjectMapperFactoryBean
実装では、これを行うことができます(SOの他の場所で解決策として文書化されているように)。
_ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, isPrettyPrint());
return mapper;
_
しかし、うまくいきませんでした。そして、なぜ悪夢なのかを解明しようとしています。ジャクソンを理解することは、忍耐の主要なテストです。そのソースコードを見ると、古くて曖昧な構成(機能をオン/オフするための整数ビットマスク?冗談でしょうか?)
基本的に、SpringのMappingJacksonHttpMessageConverter
を最初から書き直し、そのwriteInternal
の実装を次のようにオーバーライドする必要がありました。
_@Override
protected void writeInternal(Object o, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
JsonEncoding encoding = getEncoding(outputMessage.getHeaders().getContentType());
JsonGenerator jsonGenerator =
getObjectMapper().getJsonFactory().createJsonGenerator(outputMessage.getBody(), encoding);
try {
if (this.prefixJson) {
jsonGenerator.writeRaw("{} && ");
}
if (isPrettyPrint()) {
jsonGenerator.useDefaultPrettyPrinter();
}
getObjectMapper().writeValue(jsonGenerator, o);
}
catch (JsonGenerationException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
}
}
_
既存の実装に追加したのは、次のブロックのみです。
_if (isPrettyPrint()) {
jsonGenerator.useDefaultPrettyPrinter();
}
_
isPrettyPrint()
は、MappingJacksonHttpMessageConverter
サブクラスに追加した一致するセッターを備えた単なるJavaBeans互換のゲッターです。
これらのフープを飛び越えて初めて、_${json.prettyPrint}
_値(アプリのデプロイ方法に応じてプロパティとして設定されます)に基づいてプリティ印刷をオンまたはオフにできました。
これが将来誰かに役立つことを願っています!
Spring Boot 1.2以降を使用している場合、簡単な解決策は追加することです
spring.jackson.serialization.INDENT_OUTPUT=true
application.properties
ファイルに。これは、シリアル化にJacksonを使用していることを前提としています。
以前のバージョンのSpring Bootを使用している場合は、追加できます
http.mappers.json-pretty-print=true
このソリューションはSpring Boot 1.2でも動作しますが、 非推奨 であり、最終的には完全に削除されます。起動時にログに非推奨の警告が表示されます。
(spring-boot-starter-web
を使用してテスト済み)
Jackson 2.0.0を使用している場合、Lesが望んだ方法でそれを行うことができます。現在RC3を使用していますが、構成は期待どおりに機能しているようです。
ObjectMapper jacksonMapper = new ObjectMapper();
jacksonMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
翻訳する
{"foo":"foo","bar":{"field1":"field1","field2":"field2"}}
に
{
"foo" : "foo",
"bar" : {
"field1" : "field1",
"field2" : "field2"
}
}
Jacksonが生成するJSONコンテンツをきれいに印刷するにはどうすればよいですか?
以下に簡単な例を示します。
オリジナルのJSON入力:
{"one":"AAA","two":["BBB","CCC"],"three":{"four":"DDD","five":["EEE","FFF"]}}
Foo.Java:
import Java.io.FileReader;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.ObjectWriter;
public class Foo
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper();
MyClass myObject = mapper.readValue(new FileReader("input.json"), MyClass.class);
// this is Jackson 1.x API only:
ObjectWriter writer = mapper.defaultPrettyPrintingWriter();
// ***IMPORTANT!!!*** for Jackson 2.x use the line below instead of the one above:
// ObjectWriter writer = mapper.writer().withDefaultPrettyPrinter();
System.out.println(writer.writeValueAsString(myObject));
}
}
class MyClass
{
String one;
String[] two;
MyOtherClass three;
public String getOne() {return one;}
void setOne(String one) {this.one = one;}
public String[] getTwo() {return two;}
void setTwo(String[] two) {this.two = two;}
public MyOtherClass getThree() {return three;}
void setThree(MyOtherClass three) {this.three = three;}
}
class MyOtherClass
{
String four;
String[] five;
public String getFour() {return four;}
void setFour(String four) {this.four = four;}
public String[] getFive() {return five;}
void setFive(String[] five) {this.five = five;}
}
出力:
{
"one" : "AAA",
"two" : [ "BBB", "CCC" ],
"three" : {
"four" : "DDD",
"five" : [ "EEE", "FFF" ]
}
}
このアプローチがニーズに完全に適合しない場合、 API docs v1.8.1 で「pretty」を検索すると、関連するコンポーネントが利用可能になります。 APIバージョン2.xを使用する場合は、代わりに newer API 2.1.0 docs をご覧ください。
私はこのアプローチを提案するかもしれません、それはSpring 4.0.xとおそらく古いバージョンで有効です。
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import Java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
mappingJackson2HttpMessageConverter.setObjectMapper(objectMapper());
return mappingJackson2HttpMessageConverter;
}
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objMapper = new ObjectMapper();
objMapper.enable(SerializationFeature.INDENT_OUTPUT);
return objMapper;
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
super.configureMessageConverters(converters);
converters.add(mappingJackson2HttpMessageConverter());
}
}
ソリューションを提供してくれたWillie Wheelerに感謝します。 Willie WheelerのSpringブログ
プリティプリントは、MappingJackson2HttpMessageConverterコンバーターを追加して構成することで有効になります。実稼働環境内でprettyprintを無効にします。
メッセージコンバーターの構成
<mvc:annotation-driven>
<mvc:message-converters>
<bean id="jacksonHttpMessageConverter"
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="prettyPrint" value="${json.prettyPrint}" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
baeldung に基づいて、これはJava 8:
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
Optional<HttpMessageConverter<?>> converterFound;
converterFound = converters.stream().filter(c -> c instanceof AbstractJackson2HttpMessageConverter).findFirst();
if (converterFound.isPresent()) {
final AbstractJackson2HttpMessageConverter converter;
converter = (AbstractJackson2HttpMessageConverter) converterFound.get();
converter.getObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
converter.getObjectMapper().enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
}
}
ジャクソン2には優れたAPIがありますが、Spring MVCがObjectMapper#writeValue(JsonGenerator、Object)を使用してオブジェクトをJSONとして書き出すため、Spring MVC環境ではこの問題を解決できません。このwriteValueバリアントは、Jackson 1.xまたは2.0のINDENT_OUTPUTなどのObjectMapperシリアル化機能を適用しません。
これはやや紛らわしいと思います。 ObjectMapperを使用してJsonGeneratorを構築するため、返されたジェネレーターは、構成されたObjectMapper設定に基づいて初期化されると予想されます。これをJackson 2.0に対する問題としてここに報告しました: https://github.com/FasterXML/jackson-databind/issues/12 。
PrettyPrintフラグの値に基づいてJsonGenerator#useDefaultPrettyPrinterを呼び出すというLesの提案は、現時点でできる最善の方法です。先に進んで、INDENT_OUTPUT SerializationFeatureの有効状態に基づいてこれを行うJackson2 HttpMessageConverterを作成しました: https://Gist.github.com/2423129 。
上記のようにカスタムMappingJacksonHttpMessageConverterを動作させるのに問題がありましたが、設定に苦労した後、ようやく動作するようになりました。コードの観点からは、上記の内容を正確に実行しましたが、次の設定をspringapp-servlet.xmlに追加して機能させる必要がありました。
これが、同じものを実装しようとしている他の人に役立つことを願っています。
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonConverter" />
</list>
</property>
</bean>
<bean id="jsonConverter" class="com.xxx.xxx.xxx.common.PrettyPrintMappingJacksonHttpMessageConverter">
<property name="supportedMediaTypes" value="application/json" />
<property name="prettyPrint" value="true" />
</bean>
私はそれをRESTサービスの問題ではなく、レンダリングの問題にします。
誰がレンダリングをしていますか?そのコンポーネントにJSONをフォーマットさせます。たぶん、2つのURLがあります-1つは実稼働用、もう1つは開発用です。