I have a Jersey (1.x) based REST service. It uses Jackson 2.4.4 to generate JSON responses. I need to add a newline character at the end of response (cURL users complain that there's no new line in responses). I am using Jersey pretty-print feature (SerializationFeature.INDENT_OUTPUT
).
我有一个基于球衣(1.x)的休息服务。它使用Jackson 2.4.4生成JSON响应。我需要在响应末尾添加一个换行字符(cURL用户抱怨响应中没有换行)。我正在使用Jersey的漂亮打印特性(serizationfeature.indent_output)。
current: {\n "prop" : "value"\n}
当前:{\n "prop": "value"\n}
wanted: {\n "prop" : "value"\n}\n
通缉:{\n“道具”:“价值”\n
-
I tried using a custom serializer. I need to add
\n
only at the end of the root object. Serializer is defined per data type, which means, if an instance of such class is nested in a response, I will get\n
in the middle of my JSON.我尝试使用自定义序列化器。我只需要在根对象的末尾添加\n。序列化器是根据数据类型定义的,这意味着,如果此类实例嵌套在响应中,我将在JSON中获得\n。
-
I thought of subclassing
com.fasterxml.jackson.core.JsonGenerator.java
, overridingclose()
where i'd addwriteRaw('\n')
, but that feels very hacky.我想到了子类化com.fasterxml.jackson.core.JsonGenerator。在我添加writeRaw('\n')的地方,java,重写close(),但这感觉很粗糙。
-
Another idea would be to add Servlet filter which would re-write the response from Jersey Filter, adding the
\n
and incrementing the contentLenght by 1. Seems not only hacky, but also inefficient.另一个想法是添加Servlet过滤器,它将重新编写来自Jersey过滤器的响应,添加\n,并将contentLenght增加1。似乎不仅陈腐,而且低效。
-
I could also give up Jersey taking care of serializing the content and do
ObjectMapper.writeValue() + "\n"
, but this is quite intrusive to my code (need to change many places).我还可以放弃Jersey,负责序列化内容和ObjectMapper.writeValue() +“\n”,但是这对我的代码来说是很烦人的(需要更改很多地方)。
What is the clean solution for that problem?
解决这个问题的干净方法是什么?
I have found these threads for the same problem, but none of them provides solution:
我已经找到了同样问题的这些线程,但是没有一个提供解决方案:
- http://markmail.org/message/nj4aqheqobmt4o5c
- http://markmail.org/message/nj4aqheqobmt4o5c
- http://jackson-users.ning.com/forum/topics/add-newline-after-object-serialization-in-jersey
- http://jackson-users.ning.com/forum/topics/add-newline-after-object-serialization-in-jersey
Update
更新
Finally I went for @arachnid's solution with NewlineAddingPrettyPrinter
(also bumper Jackson version to 2.6.2). Sadly, it does not work out of the box with Jaskson as JAX-RS Json provider. Changed PrettyPrinter
in ObjectMapper
does not get propagated to JsonGenerator
(see here why). To make it work, I had to add ResponseFilter
which adds ObjectWriterModifier
(now I can easily toggle between pretty-print and minimal, based on input param ):
最后,我使用了NewlineAddingPrettyPrinter(同样是Jackson版本的2.6.2)来解决@arachnid的解决方案。遗憾的是,使用Jaskson作为JAX-RS Json提供程序并不是一件容易的事。ObjectMapper中更改的PrettyPrinter不会被传播到JsonGenerator(请参见这里的原因)。为了让它工作,我必须添加ResponseFilter,其中添加ObjectWriterModifier(现在我可以很容易地根据输入参数在漂亮的打印和最小值之间切换):
@Provider
public class PrettyPrintFilter extends BaseResponseFilter {
public ContainerResponse filter(ContainerRequest request, ContainerResponse response) {
ObjectWriterInjector.set(new PrettyPrintToggler(true));
return response;
}
final class PrettyPrintToggler extends ObjectWriterModifier {
private static final PrettyPrinter NO_PRETTY_PRINT = new MinimalPrettyPrinter();
private final boolean usePrettyPrint;
public PrettyPrintToggler(boolean usePrettyPrint) {
this.usePrettyPrint = usePrettyPrint;
}
@Override
public ObjectWriter modify(EndpointConfigBase<?> endpoint, MultivaluedMap<String, Object> responseHeaders,
Object valueToWrite, ObjectWriter w, JsonGenerator g) throws IOException {
if (usePrettyPrint) g.setPrettyPrinter(new NewlineAddingPrettyPrinter());
else g.setPrettyPrinter(NO_PRETTY_PRINT);
return w;
}
}
}
2 个解决方案
#1
1
Actually, wrapping up (not subclassing) JsonGenerator isn't too bad:
实际上,封装(不是子类化)JsonGenerator还不错:
public static final class NewlineAddingJsonFactory extends JsonFactory {
@Override
protected JsonGenerator _createGenerator(Writer out, IOContext ctxt) throws IOException {
return new NewlineAddingJsonGenerator(super._createGenerator(out, ctxt));
}
@Override
protected JsonGenerator _createUTF8Generator(OutputStream out, IOContext ctxt) throws IOException {
return new NewlineAddingJsonGenerator(super._createUTF8Generator(out, ctxt));
}
}
public static final class NewlineAddingJsonGenerator extends JsonGenerator {
private final JsonGenerator underlying;
private int depth = 0;
public NewlineAddingJsonGenerator(JsonGenerator underlying) {
this.underlying = underlying;
}
@Override
public void writeStartObject() throws IOException {
underlying.writeStartObject();
++depth;
}
@Override
public void writeEndObject() throws IOException {
underlying.writeEndObject();
if (--depth == 0) {
underlying.writeRaw('\n');
}
}
// ... and delegate all the other methods of JsonGenerator (CGLIB can hide this if you put in some time)
}
@Test
public void append_newline_after_end_of_json() throws Exception {
ObjectWriter writer = new ObjectMapper(new NewlineAddingJsonFactory()).writer();
assertThat(writer.writeValueAsString(ImmutableMap.of()), equalTo("{}\n"));
assertThat(writer.writeValueAsString(ImmutableMap.of("foo", "bar")), equalTo("{\"foo\":\"bar\"}\n"));
}
A servlet filter isn't necessarily too bad either, although recently the ServletOutputStream interface has been more involved to intercept properly.
servlet过滤器也不一定很糟糕,尽管最近servlet outputstream接口更需要正确地拦截。
I found doing this via PrettyPrinter problematic on earlier Jackson versions (such as your 2.4.4), in part because of the need to go through an ObjectWriter to configure it properly: only fixed in Jackson 2.6. For completeness, this is a working 2.5 solution:
我发现在早期的Jackson版本(如2.4.4)中,通过PrettyPrinter进行配置是有问题的,部分原因是需要通过ObjectWriter来正确配置它:只在Jackson 2.6中修复。为了完整性,这是一个可工作的2.5解决方案:
@Test
public void append_newline_after_end_of_json() throws Exception {
// Jackson 2.6:
// ObjectMapper mapper = new ObjectMapper()
// .setDefaultPrettyPrinter(new NewlineAddingPrettyPrinter())
// .enable(SerializationFeature.INDENT_OUTPUT);
// ObjectWriter writer = mapper.writer();
ObjectMapper mapper = new ObjectMapper();
ObjectWriter writer = mapper.writer().with(new NewlineAddingPrettyPrinter());
assertThat(writer.writeValueAsString(ImmutableMap.of()), equalTo("{}\n"));
assertThat(writer.writeValueAsString(ImmutableMap.of("foo", "bar")),
equalTo("{\"foo\":\"bar\"}\n"));
}
public static final class NewlineAddingPrettyPrinter
extends MinimalPrettyPrinter
implements Instantiatable<PrettyPrinter> {
private int depth = 0;
@Override
public void writeStartObject(JsonGenerator jg) throws IOException, JsonGenerationException {
super.writeStartObject(jg);
++depth;
}
@Override
public void writeEndObject(JsonGenerator jg, int nrOfEntries) throws IOException, JsonGenerationException {
super.writeEndObject(jg, nrOfEntries);
if (--depth == 0) {
jg.writeRaw('\n');
}
}
@Override
public PrettyPrinter createInstance() {
return new NewlineAddingPrettyPrinter();
}
}
#2
0
Not yet tested but the following should work:
还没有经过测试,但以下方法应该有效:
public class MyObjectMapper extends ObjectMapper {
_defaultPrettyPrinter = com.fasterxml.jackson.core.util.MinimalPrettyPrinter("\n");
// AND/OR
@Override
protected PrettyPrinter _defaultPrettyPrinter() {
return new com.fasterxml.jackson.core.util.MinimalPrettyPrinter("\n");
}
}
public class JerseyConfiguration extends ResourceConfig {
...
MyObjectMapper mapper = new MyObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT); //enables pretty printing
// create JsonProvider to provide custom ObjectMapper
JacksonJaxbJsonProvider provider = new JacksonJaxbJsonProvider();
provider.setMapper(mapper);
register(provider); //register so that jersey use it
}
Do not know if this is the "cleanest" solution but it feels less hacky than the others.
不知道这是否是“最干净”的解决方案,但它感觉没有其他方案那么陈腐。
Should produce something like
应当会产生类似
{\n "root" : "1"\n}\n{\n "root2" : "2"\n}
{\n "root": "1"\n] \n{\n "root2" \n}
But it seems that does not work if there is only one root element.
但如果只有一个根元素,这似乎就行不通了。
Idea is from https://gist.github.com/deverton/7743979
来自https://gist.github.com/deverton/7743979的想法是
#1
1
Actually, wrapping up (not subclassing) JsonGenerator isn't too bad:
实际上,封装(不是子类化)JsonGenerator还不错:
public static final class NewlineAddingJsonFactory extends JsonFactory {
@Override
protected JsonGenerator _createGenerator(Writer out, IOContext ctxt) throws IOException {
return new NewlineAddingJsonGenerator(super._createGenerator(out, ctxt));
}
@Override
protected JsonGenerator _createUTF8Generator(OutputStream out, IOContext ctxt) throws IOException {
return new NewlineAddingJsonGenerator(super._createUTF8Generator(out, ctxt));
}
}
public static final class NewlineAddingJsonGenerator extends JsonGenerator {
private final JsonGenerator underlying;
private int depth = 0;
public NewlineAddingJsonGenerator(JsonGenerator underlying) {
this.underlying = underlying;
}
@Override
public void writeStartObject() throws IOException {
underlying.writeStartObject();
++depth;
}
@Override
public void writeEndObject() throws IOException {
underlying.writeEndObject();
if (--depth == 0) {
underlying.writeRaw('\n');
}
}
// ... and delegate all the other methods of JsonGenerator (CGLIB can hide this if you put in some time)
}
@Test
public void append_newline_after_end_of_json() throws Exception {
ObjectWriter writer = new ObjectMapper(new NewlineAddingJsonFactory()).writer();
assertThat(writer.writeValueAsString(ImmutableMap.of()), equalTo("{}\n"));
assertThat(writer.writeValueAsString(ImmutableMap.of("foo", "bar")), equalTo("{\"foo\":\"bar\"}\n"));
}
A servlet filter isn't necessarily too bad either, although recently the ServletOutputStream interface has been more involved to intercept properly.
servlet过滤器也不一定很糟糕,尽管最近servlet outputstream接口更需要正确地拦截。
I found doing this via PrettyPrinter problematic on earlier Jackson versions (such as your 2.4.4), in part because of the need to go through an ObjectWriter to configure it properly: only fixed in Jackson 2.6. For completeness, this is a working 2.5 solution:
我发现在早期的Jackson版本(如2.4.4)中,通过PrettyPrinter进行配置是有问题的,部分原因是需要通过ObjectWriter来正确配置它:只在Jackson 2.6中修复。为了完整性,这是一个可工作的2.5解决方案:
@Test
public void append_newline_after_end_of_json() throws Exception {
// Jackson 2.6:
// ObjectMapper mapper = new ObjectMapper()
// .setDefaultPrettyPrinter(new NewlineAddingPrettyPrinter())
// .enable(SerializationFeature.INDENT_OUTPUT);
// ObjectWriter writer = mapper.writer();
ObjectMapper mapper = new ObjectMapper();
ObjectWriter writer = mapper.writer().with(new NewlineAddingPrettyPrinter());
assertThat(writer.writeValueAsString(ImmutableMap.of()), equalTo("{}\n"));
assertThat(writer.writeValueAsString(ImmutableMap.of("foo", "bar")),
equalTo("{\"foo\":\"bar\"}\n"));
}
public static final class NewlineAddingPrettyPrinter
extends MinimalPrettyPrinter
implements Instantiatable<PrettyPrinter> {
private int depth = 0;
@Override
public void writeStartObject(JsonGenerator jg) throws IOException, JsonGenerationException {
super.writeStartObject(jg);
++depth;
}
@Override
public void writeEndObject(JsonGenerator jg, int nrOfEntries) throws IOException, JsonGenerationException {
super.writeEndObject(jg, nrOfEntries);
if (--depth == 0) {
jg.writeRaw('\n');
}
}
@Override
public PrettyPrinter createInstance() {
return new NewlineAddingPrettyPrinter();
}
}
#2
0
Not yet tested but the following should work:
还没有经过测试,但以下方法应该有效:
public class MyObjectMapper extends ObjectMapper {
_defaultPrettyPrinter = com.fasterxml.jackson.core.util.MinimalPrettyPrinter("\n");
// AND/OR
@Override
protected PrettyPrinter _defaultPrettyPrinter() {
return new com.fasterxml.jackson.core.util.MinimalPrettyPrinter("\n");
}
}
public class JerseyConfiguration extends ResourceConfig {
...
MyObjectMapper mapper = new MyObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT); //enables pretty printing
// create JsonProvider to provide custom ObjectMapper
JacksonJaxbJsonProvider provider = new JacksonJaxbJsonProvider();
provider.setMapper(mapper);
register(provider); //register so that jersey use it
}
Do not know if this is the "cleanest" solution but it feels less hacky than the others.
不知道这是否是“最干净”的解决方案,但它感觉没有其他方案那么陈腐。
Should produce something like
应当会产生类似
{\n "root" : "1"\n}\n{\n "root2" : "2"\n}
{\n "root": "1"\n] \n{\n "root2" \n}
But it seems that does not work if there is only one root element.
但如果只有一个根元素,这似乎就行不通了。
Idea is from https://gist.github.com/deverton/7743979
来自https://gist.github.com/deverton/7743979的想法是