Avoid byte[] to String to byte[] conversion when pretty printing

Previously, PrettyPrintingContentModifier would convert the byte[]
content into a String and then back into a byte[]. It did so without
consideration for the content’s character set. As a result, it could
fail to preserve the correct character encoding.

This commit updates PrettyPrintingContentModifier to avoid converting
the content into a String and back into a byte[] and to work entirely
with byte arrays instead. Removing the intermediate String from the
process removes the possibility of the content becoming corrupted.

Closes gh-202
This commit is contained in:
Andy Wilkinson
2016-02-15 11:30:28 +00:00
parent 74f9e272fd
commit cfc413f7ed
2 changed files with 31 additions and 11 deletions

View File

@@ -17,8 +17,8 @@
package org.springframework.restdocs.operation.preprocess;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -60,7 +60,7 @@ public class PrettyPrintingContentModifier implements ContentModifier {
if (originalContent.length > 0) {
for (PrettyPrinter prettyPrinter : PRETTY_PRINTERS) {
try {
return prettyPrinter.prettyPrint(originalContent).getBytes();
return prettyPrinter.prettyPrint(originalContent);
}
catch (Exception ex) {
// Continue
@@ -72,24 +72,25 @@ public class PrettyPrintingContentModifier implements ContentModifier {
private interface PrettyPrinter {
String prettyPrint(byte[] content) throws Exception;
byte[] prettyPrint(byte[] content) throws Exception;
}
private static final class XmlPrettyPrinter implements PrettyPrinter {
@Override
public String prettyPrint(byte[] original) throws Exception {
public byte[] prettyPrint(byte[] original) throws Exception {
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount",
"4");
transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "yes");
StringWriter transformed = new StringWriter();
ByteArrayOutputStream transformed = new ByteArrayOutputStream();
transformer.setErrorListener(new SilentErrorListener());
transformer.transform(createSaxSource(original),
new StreamResult(transformed));
return transformed.toString();
return transformed.toByteArray();
}
private SAXSource createSaxSource(byte[] original)
@@ -145,11 +146,13 @@ public class PrettyPrintingContentModifier implements ContentModifier {
private static final class JsonPrettyPrinter implements PrettyPrinter {
private final ObjectMapper objectMapper = new ObjectMapper()
.configure(SerializationFeature.INDENT_OUTPUT, true);
@Override
public String prettyPrint(byte[] original) throws IOException {
ObjectMapper objectMapper = new ObjectMapper()
.configure(SerializationFeature.INDENT_OUTPUT, true);
return objectMapper.writeValueAsString(objectMapper.readTree(original));
public byte[] prettyPrint(byte[] original) throws IOException {
return this.objectMapper
.writeValueAsBytes(this.objectMapper.readTree(original));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 the original author or authors.
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,12 +16,17 @@
package org.springframework.restdocs.operation.preprocess;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.restdocs.test.OutputCapture;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.isEmptyString;
import static org.junit.Assert.assertThat;
@@ -68,4 +73,16 @@ public class PrettyPrintingContentModifierTests {
null), equalTo(content.getBytes()));
}
@Test
public void encodingIsPreserved() throws Exception {
Map<String, String> input = new HashMap<>();
input.put("japanese", "\u30b3\u30f3\u30c6\u30f3\u30c4");
ObjectMapper objectMapper = new ObjectMapper();
@SuppressWarnings("unchecked")
Map<String, String> output = objectMapper
.readValue(new PrettyPrintingContentModifier().modifyContent(
objectMapper.writeValueAsBytes(input), null), Map.class);
assertThat(output, is(equalTo(input)));
}
}