[serde-gson,serde-jackson] Unify JSON writer tests
This commit is contained in:
@@ -0,0 +1,81 @@
|
|||||||
|
package de.siphalor.tweed5.testutils.serde.json;
|
||||||
|
|
||||||
|
import de.siphalor.tweed5.dataapi.api.TweedDataWriter;
|
||||||
|
import de.siphalor.tweed5.dataapi.api.decoration.TweedDataCommentDecoration;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.CsvSource;
|
||||||
|
|
||||||
|
import java.io.StringWriter;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
public interface JsonWriterTest {
|
||||||
|
TweedDataWriter createPrettyJsonWriter(StringWriter stringWriter);
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource(ignoreLeadingAndTrailingWhitespace = false, textBlock = """
|
||||||
|
123,"123"
|
||||||
|
abc def ," abc def "
|
||||||
|
'line
|
||||||
|
breaks
|
||||||
|
',"line\\nbreaks\\n"
|
||||||
|
"quotes","\\"quotes\\""
|
||||||
|
""")
|
||||||
|
@SneakyThrows
|
||||||
|
default void jsonString(String string, String expected) {
|
||||||
|
var stringWriter = new StringWriter();
|
||||||
|
try (var writer = createPrettyJsonWriter(stringWriter)) {
|
||||||
|
writer.visitString(string);
|
||||||
|
}
|
||||||
|
assertThat(stringWriter.toString()).isEqualTo(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SneakyThrows
|
||||||
|
default void jsonComplex() {
|
||||||
|
var stringWriter = new StringWriter();
|
||||||
|
try (var writer = createPrettyJsonWriter(stringWriter)) {
|
||||||
|
writer.visitMapStart();
|
||||||
|
|
||||||
|
writer.visitDecoration((TweedDataCommentDecoration) () -> "The first is the best!");
|
||||||
|
writer.visitMapEntryKey("first");
|
||||||
|
writer.visitListStart();
|
||||||
|
writer.visitInt(123);
|
||||||
|
writer.visitListStart();
|
||||||
|
writer.visitBoolean(false);
|
||||||
|
writer.visitListEnd();
|
||||||
|
writer.visitListEnd();
|
||||||
|
|
||||||
|
writer.visitDecoration((TweedDataCommentDecoration) () -> "Hello\nWorld");
|
||||||
|
writer.visitDecoration((TweedDataCommentDecoration) () -> "!");
|
||||||
|
writer.visitMapEntryKey("second");
|
||||||
|
writer.visitMapStart();
|
||||||
|
writer.visitMapEntryKey("nested");
|
||||||
|
writer.visitDouble(12.34);
|
||||||
|
writer.visitMapEnd();
|
||||||
|
|
||||||
|
writer.visitMapEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(stringWriter.toString()).isEqualToNormalizingNewlines("""
|
||||||
|
{
|
||||||
|
"first__comment": "The first is the best!",
|
||||||
|
"first": [
|
||||||
|
123,
|
||||||
|
[
|
||||||
|
false
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"second__comment": [
|
||||||
|
"Hello",
|
||||||
|
"World",
|
||||||
|
"!"
|
||||||
|
],
|
||||||
|
"second": {
|
||||||
|
"nested": 12.34
|
||||||
|
}
|
||||||
|
}""");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,23 +1,22 @@
|
|||||||
package de.siphaolor.tweed5.data.gson;
|
package de.siphaolor.tweed5.data.gson;
|
||||||
|
|
||||||
import com.google.gson.stream.JsonWriter;
|
import com.google.gson.stream.JsonWriter;
|
||||||
import de.siphalor.tweed5.dataapi.api.TweedDataVisitor;
|
|
||||||
import de.siphalor.tweed5.dataapi.api.TweedDataWriteException;
|
import de.siphalor.tweed5.dataapi.api.TweedDataWriteException;
|
||||||
import de.siphalor.tweed5.dataapi.api.TweedDataWriter;
|
import de.siphalor.tweed5.dataapi.api.TweedDataWriter;
|
||||||
import de.siphalor.tweed5.dataapi.api.decoration.TweedDataCommentDecoration;
|
import de.siphalor.tweed5.dataapi.api.decoration.TweedDataCommentDecoration;
|
||||||
import de.siphalor.tweed5.dataapi.api.decoration.TweedDataDecoration;
|
import de.siphalor.tweed5.dataapi.api.decoration.TweedDataDecoration;
|
||||||
import org.jspecify.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class GsonWriter implements TweedDataWriter {
|
public class GsonWriter implements TweedDataWriter {
|
||||||
private final JsonWriter writer;
|
private final JsonWriter writer;
|
||||||
|
|
||||||
private final Deque<Context> contextStack = new ArrayDeque<>();
|
private final Deque<Context> contextStack = new ArrayDeque<>();
|
||||||
|
private final List<String> deferredFieldComments = new ArrayList<>();
|
||||||
private @Nullable String deferredFieldComment;
|
|
||||||
|
|
||||||
public GsonWriter(JsonWriter writer) {
|
public GsonWriter(JsonWriter writer) {
|
||||||
this.writer = writer;
|
this.writer = writer;
|
||||||
@@ -148,10 +147,18 @@ public class GsonWriter implements TweedDataWriter {
|
|||||||
@Override
|
@Override
|
||||||
public void visitMapEntryKey(String key) {
|
public void visitMapEntryKey(String key) {
|
||||||
try {
|
try {
|
||||||
if (deferredFieldComment != null) {
|
if (!deferredFieldComments.isEmpty()) {
|
||||||
writer.name(key + "__comment");
|
writer.name(key + "__comment");
|
||||||
writer.value(deferredFieldComment);
|
if (deferredFieldComments.size() == 1) {
|
||||||
deferredFieldComment = null;
|
writer.value(deferredFieldComments.get(0));
|
||||||
|
} else {
|
||||||
|
writer.beginArray();
|
||||||
|
for (String comment : deferredFieldComments) {
|
||||||
|
writer.value(comment);
|
||||||
|
}
|
||||||
|
writer.endArray();
|
||||||
|
}
|
||||||
|
deferredFieldComments.clear();
|
||||||
}
|
}
|
||||||
writer.name(key);
|
writer.name(key);
|
||||||
contextStack.push(Context.VALUE);
|
contextStack.push(Context.VALUE);
|
||||||
@@ -174,14 +181,25 @@ public class GsonWriter implements TweedDataWriter {
|
|||||||
@Override
|
@Override
|
||||||
public void visitDecoration(TweedDataDecoration decoration) {
|
public void visitDecoration(TweedDataDecoration decoration) {
|
||||||
if (decoration instanceof TweedDataCommentDecoration) {
|
if (decoration instanceof TweedDataCommentDecoration) {
|
||||||
if (deferredFieldComment == null) {
|
if (peekContext() == Context.MAP) {
|
||||||
deferredFieldComment = ((TweedDataCommentDecoration) decoration).comment();
|
appendDeferredComment(((TweedDataCommentDecoration) decoration).comment());
|
||||||
} else {
|
|
||||||
deferredFieldComment += "\n" + ((TweedDataCommentDecoration) decoration).comment();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void appendDeferredComment(String comment) {
|
||||||
|
int index = 0;
|
||||||
|
while (true) {
|
||||||
|
int next = comment.indexOf('\n', index);
|
||||||
|
if (next == -1) {
|
||||||
|
deferredFieldComments.add(comment.substring(index));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
deferredFieldComments.add(comment.substring(index, next));
|
||||||
|
index = next + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws Exception {
|
public void close() throws Exception {
|
||||||
writer.close();
|
writer.close();
|
||||||
|
|||||||
@@ -1,54 +1,16 @@
|
|||||||
package de.siphaolor.tweed5.data.gson;
|
package de.siphaolor.tweed5.data.gson;
|
||||||
|
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
import de.siphalor.tweed5.dataapi.api.decoration.TweedDataCommentDecoration;
|
import de.siphalor.tweed5.dataapi.api.TweedDataWriter;
|
||||||
|
import de.siphalor.tweed5.testutils.serde.json.JsonWriterTest;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
class GsonWriterTest implements JsonWriterTest {
|
||||||
|
@Override
|
||||||
class GsonWriterTest {
|
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@Test
|
public TweedDataWriter createPrettyJsonWriter(StringWriter stringWriter) {
|
||||||
void complex() {
|
return new GsonWriter(new GsonBuilder().setPrettyPrinting().create().newJsonWriter(stringWriter));
|
||||||
var stringWriter = new StringWriter();
|
|
||||||
var writer = new GsonWriter(new GsonBuilder().setPrettyPrinting().create().newJsonWriter(stringWriter));
|
|
||||||
|
|
||||||
writer.visitMapStart();
|
|
||||||
|
|
||||||
writer.visitMapEntryKey("first");
|
|
||||||
writer.visitListStart();
|
|
||||||
writer.visitInt(123);
|
|
||||||
writer.visitListStart();
|
|
||||||
writer.visitBoolean(false);
|
|
||||||
writer.visitListEnd();
|
|
||||||
writer.visitListEnd();
|
|
||||||
|
|
||||||
writer.visitDecoration((TweedDataCommentDecoration) () -> "Hello");
|
|
||||||
writer.visitDecoration((TweedDataCommentDecoration) () -> "World");
|
|
||||||
writer.visitMapEntryKey("second");
|
|
||||||
writer.visitMapStart();
|
|
||||||
writer.visitMapEntryKey("nested");
|
|
||||||
writer.visitDouble(12.34);
|
|
||||||
writer.visitMapEnd();
|
|
||||||
|
|
||||||
writer.visitMapEnd();
|
|
||||||
|
|
||||||
assertThat(stringWriter.toString()).isEqualTo("""
|
|
||||||
{
|
|
||||||
"first": [
|
|
||||||
123,
|
|
||||||
[
|
|
||||||
false
|
|
||||||
]
|
|
||||||
],
|
|
||||||
"second__comment": "Hello\\nWorld",
|
|
||||||
"second": {
|
|
||||||
"nested": 12.34
|
|
||||||
}
|
|
||||||
}""");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
package de.siphalor.tweed5.data.jackson;
|
package de.siphalor.tweed5.data.jackson;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonGenerator;
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
import de.siphalor.tweed5.dataapi.api.TweedDataVisitor;
|
|
||||||
import de.siphalor.tweed5.dataapi.api.TweedDataWriteException;
|
import de.siphalor.tweed5.dataapi.api.TweedDataWriteException;
|
||||||
import de.siphalor.tweed5.dataapi.api.TweedDataWriter;
|
import de.siphalor.tweed5.dataapi.api.TweedDataWriter;
|
||||||
import de.siphalor.tweed5.dataapi.api.decoration.TweedDataCommentDecoration;
|
import de.siphalor.tweed5.dataapi.api.decoration.TweedDataCommentDecoration;
|
||||||
import de.siphalor.tweed5.dataapi.api.decoration.TweedDataDecoration;
|
import de.siphalor.tweed5.dataapi.api.decoration.TweedDataDecoration;
|
||||||
import org.jspecify.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class JacksonWriter implements TweedDataWriter {
|
public class JacksonWriter implements TweedDataWriter {
|
||||||
private final JsonGenerator generator;
|
private final JsonGenerator generator;
|
||||||
private final CommentWriteMode commentWriteMode;
|
private final CommentWriteMode commentWriteMode;
|
||||||
|
|
||||||
private final Deque<Context> contextStack = new ArrayDeque<>();
|
private final Deque<Context> contextStack = new ArrayDeque<>();
|
||||||
private @Nullable String deferredFieldComment;
|
private final List<String> deferredFieldComments = new ArrayList<>();
|
||||||
|
|
||||||
public JacksonWriter(JsonGenerator generator, CommentWriteMode commentWriteMode) {
|
public JacksonWriter(JsonGenerator generator, CommentWriteMode commentWriteMode) {
|
||||||
this.generator = generator;
|
this.generator = generator;
|
||||||
@@ -149,10 +149,18 @@ public class JacksonWriter implements TweedDataWriter {
|
|||||||
@Override
|
@Override
|
||||||
public void visitMapEntryKey(String key) {
|
public void visitMapEntryKey(String key) {
|
||||||
try {
|
try {
|
||||||
if (deferredFieldComment != null) {
|
if (!deferredFieldComments.isEmpty()) {
|
||||||
generator.writeFieldName(key + "__comment");
|
generator.writeFieldName(key + "__comment");
|
||||||
|
if (deferredFieldComments.size() == 1) {
|
||||||
|
generator.writeString(deferredFieldComments.get(0));
|
||||||
|
} else {
|
||||||
|
generator.writeStartArray();
|
||||||
|
for (String deferredFieldComment : deferredFieldComments) {
|
||||||
generator.writeString(deferredFieldComment);
|
generator.writeString(deferredFieldComment);
|
||||||
deferredFieldComment = null;
|
}
|
||||||
|
generator.writeEndArray();
|
||||||
|
}
|
||||||
|
deferredFieldComments.clear();
|
||||||
}
|
}
|
||||||
generator.writeFieldName(key);
|
generator.writeFieldName(key);
|
||||||
contextStack.push(Context.VALUE);
|
contextStack.push(Context.VALUE);
|
||||||
@@ -180,11 +188,7 @@ public class JacksonWriter implements TweedDataWriter {
|
|||||||
break;
|
break;
|
||||||
case MAP_ENTRIES:
|
case MAP_ENTRIES:
|
||||||
if (contextStack.peek() == Context.MAP) {
|
if (contextStack.peek() == Context.MAP) {
|
||||||
if (deferredFieldComment == null) {
|
appendDeferredComment(((TweedDataCommentDecoration) decoration).comment());
|
||||||
deferredFieldComment = ((TweedDataCommentDecoration) decoration).comment();
|
|
||||||
} else {
|
|
||||||
deferredFieldComment += "\n" + ((TweedDataCommentDecoration) decoration).comment();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DOUBLE_SLASHES:
|
case DOUBLE_SLASHES:
|
||||||
@@ -199,6 +203,19 @@ public class JacksonWriter implements TweedDataWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void appendDeferredComment(String comment) {
|
||||||
|
int index = 0;
|
||||||
|
while (true) {
|
||||||
|
int next = comment.indexOf('\n', index);
|
||||||
|
if (next == -1) {
|
||||||
|
deferredFieldComments.add(comment.substring(index));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
deferredFieldComments.add(comment.substring(index, next));
|
||||||
|
index = next + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws Exception {
|
public void close() throws Exception {
|
||||||
generator.close();
|
generator.close();
|
||||||
|
|||||||
@@ -1,77 +1,26 @@
|
|||||||
package de.siphalor.tweed5.data.jackson;
|
package de.siphalor.tweed5.data.jackson;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonFactory;
|
import com.fasterxml.jackson.core.JsonFactory;
|
||||||
|
import com.fasterxml.jackson.core.util.DefaultIndenter;
|
||||||
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
|
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
|
||||||
import com.fasterxml.jackson.core.util.Separators;
|
import com.fasterxml.jackson.core.util.Separators;
|
||||||
import de.siphalor.tweed5.dataapi.api.decoration.TweedDataCommentDecoration;
|
import de.siphalor.tweed5.dataapi.api.TweedDataWriter;
|
||||||
|
import de.siphalor.tweed5.testutils.serde.json.JsonWriterTest;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
class JacksonWriterTest implements JsonWriterTest {
|
||||||
|
@Override
|
||||||
class JacksonWriterTest {
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@Test
|
public TweedDataWriter createPrettyJsonWriter(StringWriter stringWriter) {
|
||||||
void object() {
|
return new JacksonWriter(JsonFactory.builder()
|
||||||
var stringWriter = new StringWriter();
|
.build()
|
||||||
try (var generator = JsonFactory.builder().build().createGenerator(stringWriter)) {
|
.createGenerator(stringWriter)
|
||||||
generator.setPrettyPrinter(
|
.setPrettyPrinter(new DefaultPrettyPrinter()
|
||||||
new DefaultPrettyPrinter()
|
|
||||||
.withSeparators(new Separators().withObjectFieldValueSpacing(Separators.Spacing.AFTER))
|
.withSeparators(new Separators().withObjectFieldValueSpacing(Separators.Spacing.AFTER))
|
||||||
|
.withArrayIndenter(DefaultIndenter.SYSTEM_LINEFEED_INSTANCE)
|
||||||
|
), JacksonWriter.CommentWriteMode.MAP_ENTRIES
|
||||||
);
|
);
|
||||||
var writer = new JacksonWriter(generator, JacksonWriter.CommentWriteMode.MAP_ENTRIES);
|
|
||||||
writer.visitMapStart();
|
|
||||||
writer.visitDecoration((TweedDataCommentDecoration) () -> "Hello\nWorld");
|
|
||||||
writer.visitDecoration((TweedDataCommentDecoration) () -> "!");
|
|
||||||
writer.visitMapEntryKey("test");
|
|
||||||
writer.visitInt(1234);
|
|
||||||
writer.visitMapEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
assertThat(stringWriter.toString()).isEqualTo("""
|
|
||||||
{
|
|
||||||
"test__comment": "Hello\\nWorld\\n!",
|
|
||||||
"test": 1234
|
|
||||||
}""");
|
|
||||||
}
|
|
||||||
|
|
||||||
@SneakyThrows
|
|
||||||
@Test
|
|
||||||
void complex() {
|
|
||||||
var stringWriter = new StringWriter();
|
|
||||||
try (var generator = JsonFactory.builder().build().createGenerator(stringWriter)) {
|
|
||||||
generator.setPrettyPrinter(
|
|
||||||
new DefaultPrettyPrinter()
|
|
||||||
.withSeparators(new Separators().withObjectFieldValueSpacing(Separators.Spacing.AFTER))
|
|
||||||
);
|
|
||||||
var writer = new JacksonWriter(generator, JacksonWriter.CommentWriteMode.MAP_ENTRIES);
|
|
||||||
writer.visitMapStart();
|
|
||||||
writer.visitMapEntryKey("first");
|
|
||||||
writer.visitListStart();
|
|
||||||
writer.visitInt(1);
|
|
||||||
writer.visitDecoration((TweedDataCommentDecoration) () -> "not written");
|
|
||||||
writer.visitInt(2);
|
|
||||||
writer.visitListEnd();
|
|
||||||
writer.visitDecoration((TweedDataCommentDecoration) () -> "second object");
|
|
||||||
writer.visitMapEntryKey("second");
|
|
||||||
writer.visitMapStart();
|
|
||||||
writer.visitDecoration((TweedDataCommentDecoration) () -> "inner entry");
|
|
||||||
writer.visitMapEntryKey("inner");
|
|
||||||
writer.visitBoolean(true);
|
|
||||||
writer.visitMapEnd();
|
|
||||||
writer.visitMapEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
assertThat(stringWriter.toString()).isEqualTo("""
|
|
||||||
{
|
|
||||||
"first": [ 1, 2 ],
|
|
||||||
"second__comment": "second object",
|
|
||||||
"second": {
|
|
||||||
"inner__comment": "inner entry",
|
|
||||||
"inner": true
|
|
||||||
}
|
|
||||||
}""");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user