Last active
September 3, 2021 08:59
-
-
Save komiya-atsushi/832a97c84ccae7cdfc2a to your computer and use it in GitHub Desktop.
Jackson による JSON シリアライズにおいて、JS の ``JSON.stringify()`` ぽい pretty print を実現する PrettyPrinter 実装です。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ | |
{ | |
"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