在生成的 Jersey 末尾添加新行 JSON
Add new line at the end of Jersey generated JSON
我有一个基于 Jersey (1.x) 的 REST 服务。它使用 Jackson 2.4.4 生成 JSON 响应。我需要在响应末尾添加换行符(cURL 用户抱怨响应中没有换行符)。我正在使用 Jersey 漂亮打印功能 (SerializationFeature.INDENT_OUTPUT
)。
当前: {\n "prop" : "value"\n}
想要: {\n "prop" : "value"\n}\n
我尝试使用自定义序列化程序。我只需要在根对象的末尾添加 \n
。序列化器是按数据类型定义的,这意味着,如果这样的 class 的实例嵌套在响应中,我将在我的 JSON 中间得到 \n
。
我想到了 subclassing com.fasterxml.jackson.core.JsonGenerator.java
,覆盖 close()
我要添加 writeRaw('\n')
,但这感觉很老套。
另一个想法是添加 Servlet 过滤器,它将重写 Jersey 过滤器的响应,添加 \n
并将 contentLenght 增加 1。似乎不仅 hacky,而且效率低下。
我也可以放弃 Jersey 来处理内容的序列化并做 ObjectMapper.writeValue() + "\n"
,但这对我的代码来说是相当侵入性的(需要更改很多地方)。
那个问题的干净解决方案是什么?
我找到了这些线程解决同样的问题,但其中 none 提供了解决方案:
- http://markmail.org/message/nj4aqheqobmt4o5c
- http://jackson-users.ning.com/forum/topics/add-newline-after-object-serialization-in-jersey
更新
最后我用 NewlineAddingPrettyPrinter
寻求@arachnid 的解决方案(也将 Jackson 版本升级到 2.6.2)。可悲的是,它不能与 Jaskson 开箱即用,因为 JAX-RS Json provider. Changed PrettyPrinter
in ObjectMapper
does not get propagated to JsonGenerator
(see here 为什么)。为了让它工作,我必须添加 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;
}
}
}
尚未测试,但以下应该有效:
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
}
不知道这是否是 "cleanest" 解决方案,但感觉比其他解决方案更简单。
应该产生类似
的东西
{\n "root" : "1"\n}\n{\n "root2" : "2"\n}
但是,如果只有一个根元素,似乎不起作用。
实际上,结束(不是子类化)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"));
}
Servlet 过滤器也不一定太糟糕,尽管最近 ServletOutputStream 接口更多地涉及到正确拦截。
我发现在早期的 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();
}
}
我有一个基于 Jersey (1.x) 的 REST 服务。它使用 Jackson 2.4.4 生成 JSON 响应。我需要在响应末尾添加换行符(cURL 用户抱怨响应中没有换行符)。我正在使用 Jersey 漂亮打印功能 (SerializationFeature.INDENT_OUTPUT
)。
当前: {\n "prop" : "value"\n}
想要: {\n "prop" : "value"\n}\n
我尝试使用自定义序列化程序。我只需要在根对象的末尾添加
\n
。序列化器是按数据类型定义的,这意味着,如果这样的 class 的实例嵌套在响应中,我将在我的 JSON 中间得到\n
。我想到了 subclassing
com.fasterxml.jackson.core.JsonGenerator.java
,覆盖close()
我要添加writeRaw('\n')
,但这感觉很老套。另一个想法是添加 Servlet 过滤器,它将重写 Jersey 过滤器的响应,添加
\n
并将 contentLenght 增加 1。似乎不仅 hacky,而且效率低下。我也可以放弃 Jersey 来处理内容的序列化并做
ObjectMapper.writeValue() + "\n"
,但这对我的代码来说是相当侵入性的(需要更改很多地方)。
那个问题的干净解决方案是什么?
我找到了这些线程解决同样的问题,但其中 none 提供了解决方案:
- http://markmail.org/message/nj4aqheqobmt4o5c
- http://jackson-users.ning.com/forum/topics/add-newline-after-object-serialization-in-jersey
更新
最后我用 NewlineAddingPrettyPrinter
寻求@arachnid 的解决方案(也将 Jackson 版本升级到 2.6.2)。可悲的是,它不能与 Jaskson 开箱即用,因为 JAX-RS Json provider. Changed PrettyPrinter
in ObjectMapper
does not get propagated to JsonGenerator
(see here 为什么)。为了让它工作,我必须添加 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;
}
}
}
尚未测试,但以下应该有效:
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
}
不知道这是否是 "cleanest" 解决方案,但感觉比其他解决方案更简单。
应该产生类似
的东西{\n "root" : "1"\n}\n{\n "root2" : "2"\n}
但是,如果只有一个根元素,似乎不起作用。
实际上,结束(不是子类化)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"));
}
Servlet 过滤器也不一定太糟糕,尽管最近 ServletOutputStream 接口更多地涉及到正确拦截。
我发现在早期的 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();
}
}