fix(serialize-json): align handling of names in arrays with serialize-xml
ci/woodpecker/push/woodpecker Pipeline was successful
Details
ci/woodpecker/push/woodpecker Pipeline was successful
Details
This commit is contained in:
parent
4a1944f792
commit
2bcaf3336e
|
@ -110,11 +110,23 @@ public class JsonReader extends SerializeReader<IOException, JsonReader> impleme
|
|||
private String[] pathNames = new String[32];
|
||||
private int[] pathIndices = new int[32];
|
||||
|
||||
private boolean wroteName = false;
|
||||
private Heuristics heuristics = Heuristics.DEFAULT;
|
||||
|
||||
/** Creates a new instance that reads a JSON-encoded stream from {@code in}. */
|
||||
public JsonReader(Reader in) {
|
||||
this.in = Objects.requireNonNull(in, "in == null");
|
||||
}
|
||||
|
||||
public JsonReader setHeuristics(Heuristics heuristics) {
|
||||
this.heuristics = Objects.requireNonNull(heuristics);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Heuristics getHeuristics() {
|
||||
return heuristics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonReader beginArray() throws IOException {
|
||||
int p = peeked;
|
||||
|
@ -122,6 +134,7 @@ public class JsonReader extends SerializeReader<IOException, JsonReader> impleme
|
|||
p = doPeek();
|
||||
}
|
||||
if (p == PEEKED_BEGIN_ARRAY) {
|
||||
wroteName = false;
|
||||
push(JsonScope.EMPTY_ARRAY);
|
||||
pathIndices[stackSize - 1] = 0;
|
||||
peeked = PEEKED_NONE;
|
||||
|
@ -138,6 +151,7 @@ public class JsonReader extends SerializeReader<IOException, JsonReader> impleme
|
|||
p = doPeek();
|
||||
}
|
||||
if (p == PEEKED_END_ARRAY) {
|
||||
wroteName = false;
|
||||
stackSize--;
|
||||
pathIndices[stackSize - 1]++;
|
||||
peeked = PEEKED_NONE;
|
||||
|
@ -154,6 +168,7 @@ public class JsonReader extends SerializeReader<IOException, JsonReader> impleme
|
|||
p = doPeek();
|
||||
}
|
||||
if (p == PEEKED_BEGIN_OBJECT) {
|
||||
wroteName = false;
|
||||
push(JsonScope.EMPTY_OBJECT);
|
||||
peeked = PEEKED_NONE;
|
||||
return this;
|
||||
|
@ -169,6 +184,7 @@ public class JsonReader extends SerializeReader<IOException, JsonReader> impleme
|
|||
p = doPeek();
|
||||
}
|
||||
if (p == PEEKED_END_OBJECT) {
|
||||
wroteName = false;
|
||||
stackSize--;
|
||||
pathNames[stackSize] = null; // Free the last path name so that it can be garbage collected!
|
||||
pathIndices[stackSize - 1]++;
|
||||
|
@ -576,8 +592,16 @@ public class JsonReader extends SerializeReader<IOException, JsonReader> impleme
|
|||
} else if (p == PEEKED_DOUBLE_QUOTED_NAME) {
|
||||
result = nextQuotedValue('"');
|
||||
} else {
|
||||
// If we are in an array, allow reading an in inferred name once
|
||||
if (!wroteName) {
|
||||
if (stack[stackSize - 1] == JsonScope.EMPTY_ARRAY || stack[stackSize - 1] == JsonScope.NONEMPTY_ARRAY) {
|
||||
wroteName = true;
|
||||
return heuristics.guessArrayElementName(getPath());
|
||||
}
|
||||
}
|
||||
throw unexpectedTokenError("a name");
|
||||
}
|
||||
wroteName = true;
|
||||
peeked = PEEKED_NONE;
|
||||
pathNames[stackSize - 1] = result;
|
||||
return result;
|
||||
|
@ -607,6 +631,7 @@ public class JsonReader extends SerializeReader<IOException, JsonReader> impleme
|
|||
} else {
|
||||
throw unexpectedTokenError("a string");
|
||||
}
|
||||
wroteName = false;
|
||||
peeked = PEEKED_NONE;
|
||||
pathIndices[stackSize - 1]++;
|
||||
return result;
|
||||
|
@ -619,10 +644,12 @@ public class JsonReader extends SerializeReader<IOException, JsonReader> impleme
|
|||
p = doPeek();
|
||||
}
|
||||
if (p == PEEKED_TRUE) {
|
||||
wroteName = false;
|
||||
peeked = PEEKED_NONE;
|
||||
pathIndices[stackSize - 1]++;
|
||||
return true;
|
||||
} else if (p == PEEKED_FALSE) {
|
||||
wroteName = false;
|
||||
peeked = PEEKED_NONE;
|
||||
pathIndices[stackSize - 1]++;
|
||||
return false;
|
||||
|
@ -637,6 +664,7 @@ public class JsonReader extends SerializeReader<IOException, JsonReader> impleme
|
|||
p = doPeek();
|
||||
}
|
||||
if (p == PEEKED_NULL) {
|
||||
wroteName = false;
|
||||
peeked = PEEKED_NONE;
|
||||
pathIndices[stackSize - 1]++;
|
||||
} else {
|
||||
|
@ -652,6 +680,7 @@ public class JsonReader extends SerializeReader<IOException, JsonReader> impleme
|
|||
}
|
||||
|
||||
if (p == PEEKED_LONG) {
|
||||
wroteName = false;
|
||||
peeked = PEEKED_NONE;
|
||||
pathIndices[stackSize - 1]++;
|
||||
return (double) peekedLong;
|
||||
|
@ -673,6 +702,7 @@ public class JsonReader extends SerializeReader<IOException, JsonReader> impleme
|
|||
if (!serializeSpecialFloatingPointValues && (Double.isNaN(result) || Double.isInfinite(result))) {
|
||||
throw syntaxError("JSON forbids NaN and infinities: " + result);
|
||||
}
|
||||
wroteName = false;
|
||||
peekedString = null;
|
||||
peeked = PEEKED_NONE;
|
||||
pathIndices[stackSize - 1]++;
|
||||
|
@ -687,6 +717,7 @@ public class JsonReader extends SerializeReader<IOException, JsonReader> impleme
|
|||
}
|
||||
|
||||
if (p == PEEKED_LONG) {
|
||||
wroteName = false;
|
||||
peeked = PEEKED_NONE;
|
||||
pathIndices[stackSize - 1]++;
|
||||
return peekedLong;
|
||||
|
@ -703,6 +734,7 @@ public class JsonReader extends SerializeReader<IOException, JsonReader> impleme
|
|||
}
|
||||
try {
|
||||
long result = Long.parseLong(peekedString);
|
||||
wroteName = false;
|
||||
peeked = PEEKED_NONE;
|
||||
pathIndices[stackSize - 1]++;
|
||||
return result;
|
||||
|
@ -720,6 +752,7 @@ public class JsonReader extends SerializeReader<IOException, JsonReader> impleme
|
|||
throw new NumberFormatException("Expected a long but was " + peekedString + locationString());
|
||||
}
|
||||
peekedString = null;
|
||||
wroteName = false;
|
||||
peeked = PEEKED_NONE;
|
||||
pathIndices[stackSize - 1]++;
|
||||
return result;
|
||||
|
@ -738,6 +771,7 @@ public class JsonReader extends SerializeReader<IOException, JsonReader> impleme
|
|||
if (peekedLong != result) { // Make sure no precision was lost casting to 'int'.
|
||||
throw new NumberFormatException("Expected an int but was " + peekedLong + locationString());
|
||||
}
|
||||
wroteName = false;
|
||||
peeked = PEEKED_NONE;
|
||||
pathIndices[stackSize - 1]++;
|
||||
return result;
|
||||
|
@ -754,6 +788,7 @@ public class JsonReader extends SerializeReader<IOException, JsonReader> impleme
|
|||
}
|
||||
try {
|
||||
result = Integer.parseInt(peekedString);
|
||||
wroteName = false;
|
||||
peeked = PEEKED_NONE;
|
||||
pathIndices[stackSize - 1]++;
|
||||
return result;
|
||||
|
@ -775,6 +810,7 @@ public class JsonReader extends SerializeReader<IOException, JsonReader> impleme
|
|||
}
|
||||
}
|
||||
peekedString = null;
|
||||
wroteName = false;
|
||||
peeked = PEEKED_NONE;
|
||||
pathIndices[stackSize - 1]++;
|
||||
return result;
|
||||
|
@ -788,6 +824,7 @@ public class JsonReader extends SerializeReader<IOException, JsonReader> impleme
|
|||
}
|
||||
|
||||
if (p == PEEKED_LONG) {
|
||||
wroteName = false;
|
||||
peeked = PEEKED_NONE;
|
||||
pathIndices[stackSize - 1]++;
|
||||
return (double) peekedLong;
|
||||
|
@ -812,6 +849,7 @@ public class JsonReader extends SerializeReader<IOException, JsonReader> impleme
|
|||
throw syntaxError("JSON forbids NaN and infinities: " + result);
|
||||
}
|
||||
peekedString = null;
|
||||
wroteName = false;
|
||||
peeked = PEEKED_NONE;
|
||||
pathIndices[stackSize - 1]++;
|
||||
return result;
|
||||
|
@ -829,13 +867,16 @@ public class JsonReader extends SerializeReader<IOException, JsonReader> impleme
|
|||
switch (p) {
|
||||
case PEEKED_BEGIN_ARRAY:
|
||||
push(JsonScope.EMPTY_ARRAY);
|
||||
wroteName = false;
|
||||
count++;
|
||||
break;
|
||||
case PEEKED_BEGIN_OBJECT:
|
||||
push(JsonScope.EMPTY_OBJECT);
|
||||
wroteName = false;
|
||||
count++;
|
||||
break;
|
||||
case PEEKED_END_ARRAY:
|
||||
wroteName = false;
|
||||
stackSize--;
|
||||
count--;
|
||||
break;
|
||||
|
@ -846,20 +887,25 @@ public class JsonReader extends SerializeReader<IOException, JsonReader> impleme
|
|||
// Free the last path name so that it can be garbage collected
|
||||
pathNames[stackSize - 1] = null;
|
||||
}
|
||||
wroteName = false;
|
||||
stackSize--;
|
||||
count--;
|
||||
break;
|
||||
case PEEKED_UNQUOTED:
|
||||
wroteName = false;
|
||||
skipUnquotedValue();
|
||||
break;
|
||||
case PEEKED_SINGLE_QUOTED:
|
||||
wroteName = false;
|
||||
skipQuotedValue('\'');
|
||||
break;
|
||||
case PEEKED_DOUBLE_QUOTED:
|
||||
wroteName = false;
|
||||
skipQuotedValue('"');
|
||||
break;
|
||||
case PEEKED_UNQUOTED_NAME:
|
||||
skipUnquotedValue();
|
||||
wroteName = true;
|
||||
// Only update when name is explicitly skipped, otherwise stack is not updated anyways
|
||||
if (count == 0) {
|
||||
pathNames[stackSize - 1] = "<skipped>";
|
||||
|
@ -867,6 +913,7 @@ public class JsonReader extends SerializeReader<IOException, JsonReader> impleme
|
|||
break;
|
||||
case PEEKED_SINGLE_QUOTED_NAME:
|
||||
skipQuotedValue('\'');
|
||||
wroteName = true;
|
||||
// Only update when name is explicitly skipped, otherwise stack is not updated anyways
|
||||
if (count == 0) {
|
||||
pathNames[stackSize - 1] = "<skipped>";
|
||||
|
@ -874,12 +921,14 @@ public class JsonReader extends SerializeReader<IOException, JsonReader> impleme
|
|||
break;
|
||||
case PEEKED_DOUBLE_QUOTED_NAME:
|
||||
skipQuotedValue('"');
|
||||
wroteName = true;
|
||||
// Only update when name is explicitly skipped, otherwise stack is not updated anyways
|
||||
if (count == 0) {
|
||||
pathNames[stackSize - 1] = "<skipped>";
|
||||
}
|
||||
break;
|
||||
case PEEKED_NUMBER:
|
||||
wroteName = false;
|
||||
pos += peekedNumberLength;
|
||||
break;
|
||||
case PEEKED_EOF:
|
||||
|
@ -887,6 +936,7 @@ public class JsonReader extends SerializeReader<IOException, JsonReader> impleme
|
|||
default:
|
||||
// For all other tokens there is nothing to do; token has already been consumed from
|
||||
// underlying reader
|
||||
wroteName = false;
|
||||
}
|
||||
peeked = PEEKED_NONE;
|
||||
} while (count > 0);
|
||||
|
@ -1425,6 +1475,11 @@ public class JsonReader extends SerializeReader<IOException, JsonReader> impleme
|
|||
pos += 5;
|
||||
}
|
||||
|
||||
public interface Heuristics {
|
||||
String guessArrayElementName(String path);
|
||||
Heuristics DEFAULT = (path) -> "item";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
peeked = PEEKED_NONE;
|
||||
|
|
|
@ -29,6 +29,7 @@ public class JsonWriter extends SerializeWriter<IOException, JsonWriter> impleme
|
|||
private boolean omitQuotes = false;
|
||||
private String deferredName;
|
||||
private final List<String> deferredComments = new LinkedList<>();
|
||||
private boolean commentUnexpectedNames = false;
|
||||
|
||||
public JsonWriter(Writer out) {
|
||||
this.out = Objects.requireNonNull(out, "out == null");
|
||||
|
@ -83,6 +84,15 @@ public class JsonWriter extends SerializeWriter<IOException, JsonWriter> impleme
|
|||
return omitQuotes;
|
||||
}
|
||||
|
||||
public JsonWriter setCommentUnexpectedNames(boolean commentUnexpectedNames) {
|
||||
this.commentUnexpectedNames = commentUnexpectedNames;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isCommentUnexpectedNames() {
|
||||
return commentUnexpectedNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonWriter beginArray() throws IOException {
|
||||
writeDeferredName();
|
||||
|
@ -118,7 +128,8 @@ public class JsonWriter extends SerializeWriter<IOException, JsonWriter> impleme
|
|||
throw new IllegalStateException("Nesting problem.");
|
||||
}
|
||||
if (deferredName != null) {
|
||||
throw new IllegalStateException("Dangling name: " + deferredName);
|
||||
if (lenient) nullValue();
|
||||
else throw new IllegalStateException("Dangling name: " + deferredName);
|
||||
}
|
||||
|
||||
if (!deferredComments.isEmpty()) {
|
||||
|
@ -192,7 +203,11 @@ public class JsonWriter extends SerializeWriter<IOException, JsonWriter> impleme
|
|||
}
|
||||
int context = peek();
|
||||
if (context != EMPTY_OBJECT && context != NONEMPTY_OBJECT) {
|
||||
throw new IllegalStateException("Please begin an object before writing a name.");
|
||||
if (lenient) {
|
||||
if (context != EMPTY_ARRAY && context != NONEMPTY_ARRAY) throw new IllegalStateException("Please begin an object or array before writing a name.");
|
||||
} else {
|
||||
throw new IllegalStateException("Please begin an object before writing a name.");
|
||||
}
|
||||
}
|
||||
deferredName = name;
|
||||
return this;
|
||||
|
@ -200,12 +215,19 @@ public class JsonWriter extends SerializeWriter<IOException, JsonWriter> impleme
|
|||
|
||||
private void writeDeferredName() throws IOException {
|
||||
if (deferredName != null) {
|
||||
beforeName();
|
||||
if (omitQuotes && deferredName.matches("[a-zA-Z_$][\\w$]*")) {
|
||||
out.write(deferredName);
|
||||
}
|
||||
else {
|
||||
string(deferredName);
|
||||
int context = peek();
|
||||
if (context == EMPTY_ARRAY || context == NONEMPTY_ARRAY) {
|
||||
if (commentUnexpectedNames) {
|
||||
// Write the name as a comment instead of literally
|
||||
comment(deferredName);
|
||||
}
|
||||
} else {
|
||||
beforeName();
|
||||
if (omitQuotes && deferredName.matches("[a-zA-Z_$][\\w$]*")) {
|
||||
out.write(deferredName);
|
||||
} else {
|
||||
string(deferredName);
|
||||
}
|
||||
}
|
||||
deferredName = null;
|
||||
}
|
||||
|
|
|
@ -153,21 +153,41 @@ public final class JsonWriterTest {
|
|||
@Test
|
||||
public void testNameInArray() throws IOException {
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||
JsonWriter jsonWriter = new JsonWriter(stringWriter).setLenient(true);
|
||||
|
||||
jsonWriter.beginArray();
|
||||
jsonWriter.name("hello");
|
||||
IllegalStateException e =
|
||||
assertThrows(IllegalStateException.class, () -> jsonWriter.name("hello"));
|
||||
assertThat(e).hasMessageThat().isEqualTo("Please begin an object before writing a name.");
|
||||
assertThrows(IllegalStateException.class, () -> jsonWriter.name("world"));
|
||||
assertThat(e).hasMessageThat().isEqualTo("Already wrote a name, expecting a value.");
|
||||
|
||||
jsonWriter.value(12);
|
||||
e = assertThrows(IllegalStateException.class, () -> jsonWriter.name("hello"));
|
||||
assertThat(e).hasMessageThat().isEqualTo("Please begin an object before writing a name.");
|
||||
jsonWriter.name("world2");
|
||||
|
||||
jsonWriter.endArray();
|
||||
jsonWriter.close();
|
||||
|
||||
assertThat(stringWriter.toString()).isEqualTo("[12]");
|
||||
assertThat(stringWriter.toString()).isEqualTo("[12,null]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommentNameInArray() throws IOException {
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
JsonWriter jsonWriter = new JsonWriter(stringWriter).setLenient(true).setCommentUnexpectedNames(true);
|
||||
|
||||
jsonWriter.beginArray();
|
||||
jsonWriter.name("hello");
|
||||
IllegalStateException e =
|
||||
assertThrows(IllegalStateException.class, () -> jsonWriter.name("world"));
|
||||
assertThat(e).hasMessageThat().isEqualTo("Already wrote a name, expecting a value.");
|
||||
|
||||
jsonWriter.value(12);
|
||||
jsonWriter.name("world2");
|
||||
|
||||
jsonWriter.endArray();
|
||||
jsonWriter.close();
|
||||
|
||||
assertThat(stringWriter.toString()).isEqualTo("[/* hello */12,/* world2 */null]");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in New Issue