[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;
|
||||
|
||||
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.TweedDataWriter;
|
||||
import de.siphalor.tweed5.dataapi.api.decoration.TweedDataCommentDecoration;
|
||||
import de.siphalor.tweed5.dataapi.api.decoration.TweedDataDecoration;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
public class GsonWriter implements TweedDataWriter {
|
||||
private final JsonWriter writer;
|
||||
|
||||
private final Deque<Context> contextStack = new ArrayDeque<>();
|
||||
|
||||
private @Nullable String deferredFieldComment;
|
||||
private final List<String> deferredFieldComments = new ArrayList<>();
|
||||
|
||||
public GsonWriter(JsonWriter writer) {
|
||||
this.writer = writer;
|
||||
@@ -148,10 +147,18 @@ public class GsonWriter implements TweedDataWriter {
|
||||
@Override
|
||||
public void visitMapEntryKey(String key) {
|
||||
try {
|
||||
if (deferredFieldComment != null) {
|
||||
if (!deferredFieldComments.isEmpty()) {
|
||||
writer.name(key + "__comment");
|
||||
writer.value(deferredFieldComment);
|
||||
deferredFieldComment = null;
|
||||
if (deferredFieldComments.size() == 1) {
|
||||
writer.value(deferredFieldComments.get(0));
|
||||
} else {
|
||||
writer.beginArray();
|
||||
for (String comment : deferredFieldComments) {
|
||||
writer.value(comment);
|
||||
}
|
||||
writer.endArray();
|
||||
}
|
||||
deferredFieldComments.clear();
|
||||
}
|
||||
writer.name(key);
|
||||
contextStack.push(Context.VALUE);
|
||||
@@ -174,14 +181,25 @@ public class GsonWriter implements TweedDataWriter {
|
||||
@Override
|
||||
public void visitDecoration(TweedDataDecoration decoration) {
|
||||
if (decoration instanceof TweedDataCommentDecoration) {
|
||||
if (deferredFieldComment == null) {
|
||||
deferredFieldComment = ((TweedDataCommentDecoration) decoration).comment();
|
||||
} else {
|
||||
deferredFieldComment += "\n" + ((TweedDataCommentDecoration) decoration).comment();
|
||||
if (peekContext() == Context.MAP) {
|
||||
appendDeferredComment(((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
|
||||
public void close() throws Exception {
|
||||
writer.close();
|
||||
|
||||
@@ -1,54 +1,16 @@
|
||||
package de.siphaolor.tweed5.data.gson;
|
||||
|
||||
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 org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.StringWriter;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class GsonWriterTest {
|
||||
|
||||
class GsonWriterTest implements JsonWriterTest {
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@Test
|
||||
void complex() {
|
||||
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
|
||||
}
|
||||
}""");
|
||||
public TweedDataWriter createPrettyJsonWriter(StringWriter stringWriter) {
|
||||
return new GsonWriter(new GsonBuilder().setPrettyPrinting().create().newJsonWriter(stringWriter));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
package de.siphalor.tweed5.data.jackson;
|
||||
|
||||
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.TweedDataWriter;
|
||||
import de.siphalor.tweed5.dataapi.api.decoration.TweedDataCommentDecoration;
|
||||
import de.siphalor.tweed5.dataapi.api.decoration.TweedDataDecoration;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
public class JacksonWriter implements TweedDataWriter {
|
||||
private final JsonGenerator generator;
|
||||
private final CommentWriteMode commentWriteMode;
|
||||
|
||||
private final Deque<Context> contextStack = new ArrayDeque<>();
|
||||
private @Nullable String deferredFieldComment;
|
||||
private final List<String> deferredFieldComments = new ArrayList<>();
|
||||
|
||||
public JacksonWriter(JsonGenerator generator, CommentWriteMode commentWriteMode) {
|
||||
this.generator = generator;
|
||||
@@ -149,10 +149,18 @@ public class JacksonWriter implements TweedDataWriter {
|
||||
@Override
|
||||
public void visitMapEntryKey(String key) {
|
||||
try {
|
||||
if (deferredFieldComment != null) {
|
||||
if (!deferredFieldComments.isEmpty()) {
|
||||
generator.writeFieldName(key + "__comment");
|
||||
if (deferredFieldComments.size() == 1) {
|
||||
generator.writeString(deferredFieldComments.get(0));
|
||||
} else {
|
||||
generator.writeStartArray();
|
||||
for (String deferredFieldComment : deferredFieldComments) {
|
||||
generator.writeString(deferredFieldComment);
|
||||
deferredFieldComment = null;
|
||||
}
|
||||
generator.writeEndArray();
|
||||
}
|
||||
deferredFieldComments.clear();
|
||||
}
|
||||
generator.writeFieldName(key);
|
||||
contextStack.push(Context.VALUE);
|
||||
@@ -180,11 +188,7 @@ public class JacksonWriter implements TweedDataWriter {
|
||||
break;
|
||||
case MAP_ENTRIES:
|
||||
if (contextStack.peek() == Context.MAP) {
|
||||
if (deferredFieldComment == null) {
|
||||
deferredFieldComment = ((TweedDataCommentDecoration) decoration).comment();
|
||||
} else {
|
||||
deferredFieldComment += "\n" + ((TweedDataCommentDecoration) decoration).comment();
|
||||
}
|
||||
appendDeferredComment(((TweedDataCommentDecoration) decoration).comment());
|
||||
}
|
||||
break;
|
||||
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
|
||||
public void close() throws Exception {
|
||||
generator.close();
|
||||
|
||||
@@ -1,77 +1,26 @@
|
||||
package de.siphalor.tweed5.data.jackson;
|
||||
|
||||
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.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 org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.StringWriter;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class JacksonWriterTest {
|
||||
class JacksonWriterTest implements JsonWriterTest {
|
||||
@Override
|
||||
@SneakyThrows
|
||||
@Test
|
||||
void object() {
|
||||
var stringWriter = new StringWriter();
|
||||
try (var generator = JsonFactory.builder().build().createGenerator(stringWriter)) {
|
||||
generator.setPrettyPrinter(
|
||||
new DefaultPrettyPrinter()
|
||||
public TweedDataWriter createPrettyJsonWriter(StringWriter stringWriter) {
|
||||
return new JacksonWriter(JsonFactory.builder()
|
||||
.build()
|
||||
.createGenerator(stringWriter)
|
||||
.setPrettyPrinter(new DefaultPrettyPrinter()
|
||||
.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