Skip to content

Instantly share code, notes, and snippets.

@komiya-atsushi
Last active September 3, 2021 08:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save komiya-atsushi/832a97c84ccae7cdfc2a to your computer and use it in GitHub Desktop.
Save komiya-atsushi/832a97c84ccae7cdfc2a to your computer and use it in GitHub Desktop.
Jackson による JSON シリアライズにおいて、JS の ``JSON.stringify()`` ぽい pretty print を実現する PrettyPrinter 実装です。
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.PrettyPrinter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Stack;
/**
* Google Chrome における JSON.stringify() に近い pretty print を実現します。
* <p>
* このオブジェクトはスレッドセーフではありません。
* </p>
*
* @author KOMIYA Atsushi
*/
public class JsonStringifyPrettyPrinter implements PrettyPrinter {
enum State {
ROOT_VALUE_SEPARATOR,
START_OBJECT,
END_OBJECT,
OBJECT_ENTRY_SEPARATOR,
OBJECT_FIELD_VALUES_SEPARATOR,
START_ARRAY,
END_ARRAY,
ARRAY_VALUE_SEPARATOR,
BEFORE_ARRAY_VALUES,
BEFORE_OBJECT_ENTRIES
}
private static final String LINE_SEPARATOR = System.lineSeparator();
private static final char[] SPACES = new char[128];
static {
Arrays.fill(SPACES, ' ');
}
private final int numSpacesPerIndent;
private int indentLevel;
private State lastState;
private Stack<Boolean> listContainsLiteralOnly = new Stack<>();
/**
* インデントのときのスペース個数を指定して、JsonStringifyPrettyPrinter オブジェクトを生成します。
*
* @param numSpacesPerIndent インデントのときのスペース個数を指定します
*/
public JsonStringifyPrettyPrinter(int numSpacesPerIndent) {
this.numSpacesPerIndent = numSpacesPerIndent;
}
private void indent(JsonGenerator jg) throws IOException {
jg.writeRaw(LINE_SEPARATOR);
int numSpacesToBeOutput = indentLevel * numSpacesPerIndent;
while (numSpacesToBeOutput > SPACES.length) {
jg.writeRaw(SPACES, 0, SPACES.length);
numSpacesToBeOutput -= SPACES.length;
}
jg.writeRaw(SPACES, 0, numSpacesToBeOutput);
}
@Override
public void writeRootValueSeparator(JsonGenerator jg) throws IOException {
lastState = State.ROOT_VALUE_SEPARATOR;
}
@Override
public void writeStartObject(JsonGenerator jg) throws IOException {
if (lastState != State.OBJECT_FIELD_VALUES_SEPARATOR) {
indent(jg);
}
jg.writeRaw("{");
indentLevel++;
if (!listContainsLiteralOnly.empty() && listContainsLiteralOnly.peek()) {
listContainsLiteralOnly.pop();
listContainsLiteralOnly.push(false);
}
lastState = State.START_OBJECT;
}
@Override
public void writeEndObject(JsonGenerator jg, int nrOfEntries) throws IOException {
indentLevel--;
indent(jg);
jg.writeRaw("}");
lastState = State.END_OBJECT;
}
@Override
public void writeObjectEntrySeparator(JsonGenerator jg) throws IOException {
jg.writeRaw(",");
indent(jg);
lastState = State.OBJECT_ENTRY_SEPARATOR;
}
@Override
public void writeObjectFieldValueSeparator(JsonGenerator jg) throws IOException {
jg.writeRaw(": ");
lastState = State.OBJECT_FIELD_VALUES_SEPARATOR;
}
@Override
public void writeStartArray(JsonGenerator jg) throws IOException {
if (lastState != State.OBJECT_FIELD_VALUES_SEPARATOR) {
indent(jg);
}
jg.writeRaw("[");
indentLevel++;
listContainsLiteralOnly.push(true);
lastState = State.START_ARRAY;
}
@Override
public void writeEndArray(JsonGenerator jg, int nrOfValues) throws IOException {
indentLevel--;
if (!listContainsLiteralOnly.pop()) {
indent(jg);
}
jg.writeRaw("]");
lastState = State.END_ARRAY;
}
@Override
public void writeArrayValueSeparator(JsonGenerator jg) throws IOException {
jg.writeRaw(", ");
lastState = State.ARRAY_VALUE_SEPARATOR;
}
@Override
public void beforeArrayValues(JsonGenerator jg) throws IOException {
lastState = State.BEFORE_ARRAY_VALUES;
}
@Override
public void beforeObjectEntries(JsonGenerator jg) throws IOException {
indent(jg);
lastState = State.BEFORE_OBJECT_ENTRIES;
}
}
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.*;
/**
* JsonStringifyPrettyPrinter 使い方デモ。
*/
public class JsonStringifyPrettyPrinterDemo {
public static void main(String[] args) throws JsonProcessingException {
List<Map<String, Object>> object = new ArrayList<>();
Map<String, Object> map1 = new HashMap<>();
map1.put("str", "hogefuga");
map1.put("int", 123);
map1.put("list", Arrays.asList(1, 1, 2, 3, 5));
object.add(map1);
Map<String, Object> map2 = new HashMap<>();
map2.put("bool", true);
map2.put("null", null);
object.add(map2);
// ObjectMapper#writer() で、PrettyPrinter オブジェクトを設定してあげる
String json = new ObjectMapper()
.writer(new JsonStringifyPrettyPrinter(2))
.writeValueAsString(object);
System.out.println(json);
}
}
[
{
"str": "hogefuga",
"list": [1, 1, 2, 3, 5],
"int": 123
},
{
"bool": true,
"null": null
}
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment