New Json Parser

This commit is contained in:
Grahame Grieve 2022-11-16 15:11:33 -03:00
parent dbef068730
commit 011e2488a3
18 changed files with 2044 additions and 2 deletions

View File

@ -717,7 +717,10 @@ public class Utilities {
return result;
}
public static boolean isTokenChar(char ch) {
return isAlphabetic(ch) || (ch == '_');
}
public static boolean isDigit(char c) {
return (c >= '0') && (c <= '9');
}

View File

@ -0,0 +1,9 @@
package org.hl7.fhir.utilities.json;
public class JsonException extends Exception {
public JsonException(String msg) {
// TODO Auto-generated constructor stub
}
}

View File

@ -0,0 +1,89 @@
package org.hl7.fhir.utilities.json.model;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.utilities.json.JsonException;
public class JsonArray extends JsonElement {
private List<JsonElement> items = new ArrayList<>();
private List<Boolean> noCommas; // validator use
private List<Boolean> unQuoted; // validator use
public List<String> asStrings() {
List<String> list = new ArrayList<>();
for (JsonElement n : items) {
if (n instanceof JsonPrimitive) {
list.add(n.toString());
}
}
return list;
}
public List<JsonElement> getItems() {
return items;
}
public List<JsonObject> asObjects() {
List<JsonObject> list = new ArrayList<>();
for (JsonElement n : items) {
if (n instanceof JsonObject) {
list.add((JsonObject) n);
}
}
return list;
}
public JsonElementType elementType() {
return JsonElementType.ARRAY;
}
public JsonArray add(JsonElement node) throws JsonException {
check(node != null, "null object in JsonArray.add()");
items.add(node);
return this;
}
public JsonArray add(String value) throws JsonException {
check(value != null, "null value in JsonArray.add()");
items.add(new JsonString(value));
return this;
}
public Integer size() {
return items.size();
}
public boolean isNoComma(int i) {
return noCommas == null ? false : noCommas.get(i);
}
public boolean isUnquoted(int i) {
return unQuoted == null ? false : unQuoted.get(i);
}
// for the parser only
public void addForParser(JsonElement e, boolean itemNoComma, boolean unquoted) throws JsonException {
check(e != null, "null object in JsonArray.add()");
items.add(e);
if (noCommas == null) {
noCommas = new ArrayList<>();
}
noCommas.add(itemNoComma);
if (unQuoted == null) {
unQuoted = new ArrayList<>();
}
unQuoted.add(unquoted);
}
public JsonObject findByStringProp(JsonArray arr, String prop, String value) {
for (JsonObject obj : asObjects()) {
if (obj.has(prop) && value.equals(obj.getString(prop)))
return obj;
}
return null;
}
}

View File

@ -0,0 +1,31 @@
package org.hl7.fhir.utilities.json.model;
public class JsonBoolean extends JsonPrimitive {
private boolean value;
public JsonBoolean(boolean value) {
super();
this.value = value;
}
public JsonElementType elementType() {
return JsonElementType.BOOLEAN;
}
public boolean isValue() {
return value;
}
public void setValue(boolean value) {
this.value = value;
}
public String getValue() {
return value ? "true" : "false";
}
@Override
public String toString() {
return getValue();
}
}

View File

@ -0,0 +1,5 @@
package org.hl7.fhir.utilities.json.model;
public class JsonComment {
}

View File

@ -0,0 +1,48 @@
package org.hl7.fhir.utilities.json.model;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.utilities.json.JsonException;
public abstract class JsonElement {
private List<String> comments;
private JsonLocationData start;
private JsonLocationData end;
public abstract JsonElementType elementType();
public List<String> getComments() {
if (comments == null ) {
comments = new ArrayList<>();
}
return comments;
}
public JsonLocationData getStart() {
return start;
}
public void setStart(JsonLocationData start) {
this.start = start;
}
public JsonLocationData getEnd() {
return end;
}
public void setEnd(JsonLocationData end) {
this.end = end;
}
protected void check(boolean test, String msg) throws JsonException {
if (!test) {
throw new JsonException(msg);
}
}
public boolean hasComments() {
return comments != null && !comments.isEmpty();
}
}

View File

@ -0,0 +1,5 @@
package org.hl7.fhir.utilities.json.model;
public enum JsonElementType {
OBJECT, ARRAY, STRING, NUMBER, BOOLEAN, NULL;
}

View File

@ -0,0 +1,59 @@
package org.hl7.fhir.utilities.json.model;
public class JsonLocationData {
private int line;
private int col;
private int lastCol;
public JsonLocationData(int line, int col) {
super();
this.line = line;
this.col = col;
this.lastCol = col;
}
public int getLine() {
return line;
}
public int getCol() {
return col;
}
public void newLine() {
line++;
lastCol = col;
col = 1;
}
public JsonLocationData copy() {
return new JsonLocationData(line, col);
}
public void incCol() {
col++;
}
@Override
public String toString() {
return "(" + line + ", " + col + ")";
}
public JsonLocationData prev() {
if (col == 1) {
return new JsonLocationData(line-1, lastCol);
} else {
return new JsonLocationData(line, col-1);
}
}
public void back() {
if (col == 1) {
line--;
line = lastCol;
} else {
col--;
}
}
}

View File

@ -0,0 +1,18 @@
package org.hl7.fhir.utilities.json.model;
public class JsonNull extends JsonPrimitive {
public JsonElementType elementType() {
return JsonElementType.NULL;
}
public String getValue() {
return "null";
}
@Override
public String toString() {
return getValue();
}
}

View File

@ -0,0 +1,33 @@
package org.hl7.fhir.utilities.json.model;
import org.hl7.fhir.utilities.Utilities;
public class JsonNumber extends JsonPrimitive {
private String value;
public JsonNumber(String value) {
this.value = value;
}
public JsonElementType elementType() {
return JsonElementType.NUMBER;
}
public String getValue() {
return value;
}
@Override
public String toString() {
return value;
}
public Integer getInteger() {
if (Utilities.isInteger(value)) {
return Integer.parseInt(value);
} else {
return null;
}
}
}

View File

@ -0,0 +1,127 @@
package org.hl7.fhir.utilities.json.model;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hl7.fhir.utilities.json.JsonException;
public class JsonObject extends JsonElement {
private List<JsonProperty> properties = new ArrayList<>();
private Map<String, JsonProperty> propMap = new HashMap<>();
public JsonElementType elementType() {
return JsonElementType.OBJECT;
}
public JsonObject add(String name, JsonElement value) throws JsonException {
check(name != null, "Numm is null");
check(value != null, "Numm is null");
check(get(name) == null, "Name '"+name+"' already exists");
JsonProperty p = new JsonProperty(name, value);
properties.add(p);
propMap.put(name, p);
return this;
}
// this is used by the parser which can allow duplicates = true (for the validator). You should not otherwise use it
public JsonObject addForParser(String name, JsonElement value, boolean noComma, boolean nameUnquoted, boolean valueUnquoted) throws JsonException {
check(name != null, "Numm is null");
check(value != null, "Numm is null");
JsonProperty p = new JsonProperty(name, value);
p.setNoComma(noComma);
p.setUnquotedName(nameUnquoted);
p.setUnquotedValue(valueUnquoted);
properties.add(p);
propMap.put(name, p); // last duplicate wins
return this;
}
public JsonObject add(String name, String value) throws JsonException {
return add(name, new JsonString(value));
}
public JsonObject add(String name, boolean value) throws JsonException {
return add(name, new JsonBoolean(value));
}
public JsonElement get(String name) {
if (propMap.containsKey(name)) {
return propMap.get(name).getValue();
} else {
return null;
}
}
public boolean has(String name) {
return propMap.containsKey(name);
}
public List<JsonProperty> getProperties() {
return properties;
}
public String str(String name) {
if (has(name)) {
return get(name).toString();
} else {
return null;
}
}
public JsonObject getObj(String name) {
return (JsonObject) get(name);
}
public JsonString getStr(String name) {
return (JsonString) get(name);
}
public JsonBoolean getBool(String name) {
return (JsonBoolean) get(name);
}
public JsonNumber getNum(String name) {
return (JsonNumber) get(name);
}
public JsonNull getNull(String name) {
return (JsonNull) get(name);
}
public JsonArray getArr(String name) {
return (JsonArray) get(name);
}
public Integer getInteger(String name) {
return ((JsonNumber) get(name)).getInteger();
}
public String getString(String name) {
return ((JsonPrimitive) get(name)).toString();
}
public Boolean getBoolean(String name) {
return ((JsonBoolean) get(name)).isValue();
}
public JsonObject forceObj(String name) throws JsonException {
if (!has(name)) {
add(name, new JsonObject());
}
return getObj(name);
}
public JsonArray forceArr(String name) throws JsonException {
if (!has(name)) {
add(name, new JsonArray());
}
return getArr(name);
}
}

View File

@ -0,0 +1,5 @@
package org.hl7.fhir.utilities.json.model;
public abstract class JsonPrimitive extends JsonElement {
}

View File

@ -0,0 +1,53 @@
package org.hl7.fhir.utilities.json.model;
public class JsonProperty {
private String name;
private JsonElement value;
boolean noComma; // parse in Json5 mode, but records this so the validator can complain
boolean unquotedName;
boolean unquotedValue;
public JsonProperty(String name, JsonElement value) {
super();
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public JsonElement getValue() {
return value;
}
public void setValue(JsonElement value) {
this.value = value;
}
public boolean isNoComma() {
return noComma;
}
public void setNoComma(boolean noComma) {
this.noComma = noComma;
}
public boolean isUnquotedName() {
return unquotedName;
}
public void setUnquotedName(boolean unquotedName) {
this.unquotedName = unquotedName;
}
public boolean isUnquotedValue() {
return unquotedValue;
}
public void setUnquotedValue(boolean unquotedValue) {
this.unquotedValue = unquotedValue;
}
}

View File

@ -0,0 +1,28 @@
package org.hl7.fhir.utilities.json.model;
public class JsonString extends JsonPrimitive {
private String value;
public JsonString(String value) {
super();
this.value = value;
}
public JsonElementType elementType() {
return JsonElementType.STRING;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
@Override
public String toString() {
return value;
}
}

View File

@ -0,0 +1,297 @@
package org.hl7.fhir.utilities.json.parser;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.json.model.JsonElement;
import org.hl7.fhir.utilities.json.model.JsonLocationData;
public class JsonLexer {
public static class State {
private String name;
private boolean isProp;
public State(String name, boolean isProp) {
super();
this.name = name;
this.isProp = isProp;
}
public String getName() {
return name;
}
public boolean isProp() {
return isProp;
}
}
public enum TokenType {
Open, Close, String, Number, Colon, Comma, OpenArray, CloseArray, Eof, Null, Boolean;
boolean isValueType() {
return this == Open || this == String || this == Number || this == OpenArray || this == Boolean || this == Null;
}
}
private String source;
private int cursor;
private String peek;
private String value;
private TokenType type;
private Stack<State> states = new Stack<State>();
private JsonLocationData lastLocationBWS;
private JsonLocationData lastLocationAWS;
private JsonLocationData location;
private StringBuilder b = new StringBuilder();
private boolean allowComments;
private boolean allowUnquotedStrings;
private List<String> comments = new ArrayList<>();
private boolean isUnquoted;
public JsonLexer(String source, boolean allowComments, boolean allowUnquotedStrings) throws IOException {
this.source = source;
this.allowComments = allowComments;
this.allowUnquotedStrings = allowUnquotedStrings;
cursor = -1;
location = new JsonLocationData(1, 1);
start();
}
private boolean more() {
return peek != null || cursor < source.length();
}
private String getNext(int length) throws IOException {
String result = "";
if (peek != null) {
if (peek.length() > length) {
result = peek.substring(0, length);
peek = peek.substring(length);
} else {
result = peek;
peek = null;
}
}
if (result.length() < length) {
int len = length - result.length();
if (cursor > source.length() - len)
throw error("Attempt to read past end of source");
result = result + source.substring(cursor+1, cursor+len+1);
cursor = cursor + len;
}
for (char ch : result.toCharArray())
if (ch == '\n')
location.newLine();
else
location.incCol();
return result;
}
private char getNextChar() throws IOException {
char ch;
if (peek != null) {
ch = peek.charAt(0);
peek = peek.length() == 1 ? null : peek.substring(1);
} else {
cursor++;
if (cursor >= source.length()) {
ch = 0;
} else {
ch = source.charAt(cursor);
}
}
if (ch == '\n') {
location.newLine();
} else {
location.incCol();
}
return ch;
}
private void push(char ch){
peek = peek == null ? String.valueOf(ch) : String.valueOf(ch)+peek;
location.back();
}
public IOException error(String msg) {
return new IOException("Error parsing JSON source: "+msg+" at Line "+Integer.toString(location.getLine())+" (path=["+path()+"])");
}
private String path() {
if (states.empty())
return value;
else {
String result = "";
for (State s : states)
result = result + '/'+ s.getName();
result = result + value;
return result;
}
}
public void start() throws IOException {
// char ch = getNextChar();
// if (ch = '\.uEF')
// begin
// // skip BOM
// getNextChar();
// getNextChar();
// end
// else
// push(ch);
next();
}
public TokenType getType() {
return type;
}
public String getValue() {
return value;
}
public JsonLocationData getLastLocationBWS() {
return lastLocationBWS;
}
public JsonLocationData getLastLocationAWS() {
return lastLocationAWS;
}
public void next() throws IOException {
lastLocationBWS = location.copy();
char ch;
do {
ch = getNextChar();
if (allowComments && ch == '/') {
char ch1 = getNextChar();
if (ch1 == '/') {
StringBuilder b = new StringBuilder();
boolean first = true;
while (more() && !Utilities.charInSet(ch, '\r', '\n')) {
if (first) first = false; else b.append(ch);
ch = getNextChar();
}
comments.add(b.toString().trim());
} else {
push(ch1);
}
}
} while (more() && Utilities.charInSet(ch, ' ', '\r', '\n', '\t'));
lastLocationAWS = location.copy().prev();
isUnquoted = false;
if (!more()) {
type = TokenType.Eof;
} else {
switch (ch) {
case '{' :
type = TokenType.Open;
break;
case '}' :
type = TokenType.Close;
break;
case '"' :
type = TokenType.String;
b.setLength(0);
do {
ch = getNextChar();
if (ch == '\\') {
ch = getNextChar();
switch (ch) {
case '"': b.append('"'); break;
case '\'': b.append('\''); break;
case '\\': b.append('\\'); break;
case '/': b.append('/'); break;
case 'n': b.append('\n'); break;
case 'r': b.append('\r'); break;
case 't': b.append('\t'); break;
case 'u': b.append((char) Integer.parseInt(getNext(4), 16)); break;
default :
throw error("unknown escape sequence: \\"+ch);
}
ch = ' ';
} else if (ch != '"')
b.append(ch);
} while (more() && (ch != '"'));
if (!more())
throw error("premature termination of json stream during a string");
value = b.toString();
break;
case ':' :
type = TokenType.Colon;
break;
case ',' :
type = TokenType.Comma;
break;
case '[' :
type = TokenType.OpenArray;
break;
case ']' :
type = TokenType.CloseArray;
break;
default:
if ((ch >= '0' && ch <= '9') || ch == '-') {
type = TokenType.Number;
b.setLength(0);
while (more() && ((ch >= '0' && ch <= '9') || ch == '-' || ch == '.') || ch == '+' || ch == 'e' || ch == 'E') {
b.append(ch);
ch = getNextChar();
}
value = b.toString();
push(ch);
} else if (Utilities.isAlphabetic(ch) || (ch == '_')) {
type = TokenType.String;
isUnquoted = true;
b.setLength(0);
while (more() && (Utilities.isAlphabetic(ch) || Utilities.isDigit(ch) || Utilities.existsInList(ch, '_', '.', '-'))) {
b.append(ch);
ch = getNextChar();
}
value = b.toString();
push(ch);
if ("true".equals(value) || "false".equals(value)) {
this.type = TokenType.Boolean;
isUnquoted = false;
} else if ("null".equals(value)) {
this.type = TokenType.Null;
isUnquoted = false;
} else if (!allowUnquotedStrings) {
throw error("Unexpected token '"+value+"' in json stream");
}
}
}
}
}
public String consume(TokenType type) throws IOException {
if (this.type != type)
throw error("JSON syntax error - found "+this.type.toString()+" expecting "+type.toString());
String result = value;
next();
return result;
}
public JsonLocationData getLocation() {
return location;
}
public Stack<State> getStates() {
return states;
}
public void takeComments(JsonElement child) {
if (!comments.isEmpty()) {
child.getComments().addAll(comments);
comments.clear();
}
}
public boolean isUnquoted() {
return isUnquoted;
}
}

View File

@ -0,0 +1,645 @@
package org.hl7.fhir.utilities.json.parser;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import org.hl7.fhir.utilities.SimpleHTTPClient;
import org.hl7.fhir.utilities.SimpleHTTPClient.HTTPResult;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.json.JsonException;
import org.hl7.fhir.utilities.json.model.JsonArray;
import org.hl7.fhir.utilities.json.model.JsonBoolean;
import org.hl7.fhir.utilities.json.model.JsonElement;
import org.hl7.fhir.utilities.json.model.JsonElementType;
import org.hl7.fhir.utilities.json.model.JsonLocationData;
import org.hl7.fhir.utilities.json.model.JsonNull;
import org.hl7.fhir.utilities.json.model.JsonNumber;
import org.hl7.fhir.utilities.json.model.JsonObject;
import org.hl7.fhir.utilities.json.model.JsonPrimitive;
import org.hl7.fhir.utilities.json.model.JsonProperty;
import org.hl7.fhir.utilities.json.model.JsonString;
import org.hl7.fhir.utilities.json.parser.JsonLexer.State;
import org.hl7.fhir.utilities.json.parser.JsonLexer.TokenType;
/**
* Simple parser for JSON. This parser is not particularly quick (though it's not slow)
* The focus for this parser is to faithfully record the line/col number of json elements
* so that the FHIR validator can report issues by line number
*
* Also, for the validator, the parser will accept duplicate property names
*
* JSON5: When running in Json5 mode, the parser accepts
* * unquoted strings for both fields and values
* * missing commas in objects and arrays
* * comments - anything starting // will be processed as a comma to the end of the line
*
* The FHIR Validator uses this parser in Json5 mode, and the object model is marked up
* with deviations from base JSON spec so that the validator can record them as errors
* (this is better than blowing up parsing the JSON)
*
* @author grahamegrieve
*
*/
public class JsonParser {
public static JsonObject parseObject(InputStream stream) throws IOException, JsonException {
return parseObject(TextFile.streamToString(stream));
}
public static JsonObject parseObject(byte[] stream) throws IOException, JsonException {
return parseObject(TextFile.bytesToString(stream));
}
public static JsonObject parseObject(String source) throws IOException, JsonException {
return parseObject(source, false);
}
public static JsonObject parseObject(File source) throws IOException, JsonException {
return parseObject(TextFile.fileToString(source));
}
public static JsonObject parseObjectFromFile(String source) throws IOException, JsonException {
return parseObject(TextFile.fileToString(source));
}
public static JsonObject parseObjectFromUrl(String source) throws IOException, JsonException {
return parseObject(fetch(source));
}
public static JsonObject parseObject(InputStream stream, boolean isJson5) throws IOException, JsonException {
return parseObject(TextFile.streamToString(stream), isJson5);
}
public static JsonObject parseObject(byte[] stream, boolean isJson5) throws IOException, JsonException {
return parseObject(TextFile.bytesToString(stream), isJson5);
}
public static JsonObject parseObject(String source, boolean isJson5) throws IOException, JsonException {
return parseObject(source, isJson5, false);
}
public static JsonObject parseObjectFromUrl(String source, boolean isJson5) throws IOException, JsonException {
return parseObject(fetch(source), isJson5);
}
public static JsonObject parseObject(InputStream stream, boolean isJson5, boolean allowDuplicates) throws IOException, JsonException {
return parseObject(TextFile.streamToString(stream), isJson5, allowDuplicates);
}
public static JsonObject parseObject(byte[] stream, boolean isJson5, boolean allowDuplicates) throws IOException, JsonException {
return parseObject(TextFile.bytesToString(stream), isJson5, allowDuplicates);
}
public static JsonObject parseObject(String source, boolean isJson5, boolean allowDuplicates) throws IOException, JsonException {
return new JsonParser().parseJsonObject(source, isJson5, allowDuplicates);
}
// ================================================================
public static JsonElement parse(InputStream stream) throws IOException, JsonException {
return parse(TextFile.streamToString(stream));
}
public static JsonElement parse(byte[] stream) throws IOException, JsonException {
return parse(TextFile.bytesToString(stream));
}
public static JsonElement parse(String source) throws IOException, JsonException {
return parse(source, false);
}
public static JsonElement parse(File source) throws IOException, JsonException {
return parse(TextFile.fileToString(source));
}
public static JsonElement parseFromFile(String source) throws IOException, JsonException {
return parse(TextFile.fileToString(source));
}
public static JsonElement parseFromUrl(String source) throws IOException, JsonException {
return parse(fetch(source));
}
public static JsonElement parse(InputStream stream, boolean isJson5) throws IOException, JsonException {
return parse(TextFile.streamToString(stream), isJson5);
}
public static JsonElement parse(byte[] stream, boolean isJson5) throws IOException, JsonException {
return parse(TextFile.bytesToString(stream), isJson5);
}
public static JsonElement parse(String source, boolean isJson5) throws IOException, JsonException {
return parse(source, isJson5, false);
}
public static JsonElement parseFromUrl(String source, boolean isJson5) throws IOException, JsonException {
return parse(fetch(source), isJson5);
}
public static JsonElement parse(InputStream stream, boolean isJson5, boolean allowDuplicates) throws IOException, JsonException {
return parse(TextFile.streamToString(stream), isJson5, allowDuplicates);
}
public static JsonElement parse(byte[] stream, boolean isJson5, boolean allowDuplicates) throws IOException, JsonException {
return parse(TextFile.bytesToString(stream), isJson5, allowDuplicates);
}
public static JsonElement parse(String source, boolean isJson5, boolean allowDuplicates) throws IOException, JsonException {
return new JsonParser().parseJsonElement(source, isJson5, allowDuplicates);
}
// ================================================================
public static String compose(JsonElement element) {
return compose(element, false);
}
public static void compose(JsonElement element, OutputStream stream) throws IOException {
compose(element, stream, false);
}
public static byte[] composeBytes(JsonElement element) {
return composeBytes(element, false);
}
public static String compose(JsonElement element, boolean pretty) {
return new JsonParser().write(element, pretty);
}
public static void compose(JsonElement element, OutputStream stream, boolean pretty) throws IOException {
byte[] cnt = composeBytes(element, pretty);
stream.write(cnt);
}
public static byte[] composeBytes(JsonElement element, boolean pretty) {
String s = compose(element);
return s.getBytes(StandardCharsets.UTF_8);
}
// ================================================================
enum ItemType {
Object, String, Number, Boolean, Array, End, Eof, Null;
}
private JsonLexer lexer;
private ItemType itemType = ItemType.Object;
private String itemName;
private String itemValue;
private boolean allowDuplicates = true;
private boolean allowComments = false;
private boolean allowNoComma = false;
private JsonLocationData startProperty;
private JsonLocationData endProperty;
private boolean itemNoComma;
private boolean allowUnquotedStrings;
private boolean itemUnquoted;
private boolean valueUnquoted;
private JsonObject parseJsonObject(String source, boolean isJson5, boolean allowDuplicates) throws IOException, JsonException {
this.allowDuplicates = allowDuplicates;
this.allowComments = isJson5;
this.allowNoComma = isJson5;
this.allowUnquotedStrings = isJson5;
return parseSource(Utilities.stripBOM(source));
}
private JsonObject parseSource(String source) throws IOException, JsonException {
lexer = new JsonLexer(source, allowComments, allowUnquotedStrings);
JsonObject result = new JsonObject();
lexer.takeComments(result);
result.setStart(lexer.getLastLocationAWS().copy());
if (lexer.getType() == TokenType.Open) {
lexer.next();
lexer.getStates().push(new State("", true));
}
else
throw lexer.error("Unexpected content at start of JSON: "+lexer.getType().toString());
if (lexer.getType() != TokenType.Close) {
parseProperty();
readObject("$", result, true);
}
result.setEnd(endProperty != null ? endProperty.copy() : lexer.getLocation().copy());
return result;
}
private JsonElement parseJsonElement(String source, boolean isJson5, boolean allowDuplicates) throws IOException, JsonException {
this.allowDuplicates = allowDuplicates;
this.allowComments = isJson5;
this.allowNoComma = isJson5;
this.allowUnquotedStrings = isJson5;
return parseSourceElement(Utilities.stripBOM(source));
}
private JsonElement parseSourceElement(String source) throws IOException, JsonException {
lexer = new JsonLexer(source, allowComments, allowUnquotedStrings);
switch (lexer.getType()) {
case Boolean:
JsonBoolean bool = new JsonBoolean(lexer.getValue().equals("true"));
lexer.takeComments(bool);
bool.setStart(lexer.getLastLocationAWS().copy());
bool.setEnd(endProperty != null ? endProperty.copy() : lexer.getLocation().copy());
return bool;
case Null:
JsonNull nll = new JsonNull();
lexer.takeComments(nll);
nll.setStart(lexer.getLastLocationAWS().copy());
nll.setEnd(endProperty != null ? endProperty.copy() : lexer.getLocation().copy());
return nll;
case Number:
JsonNumber num = new JsonNumber(lexer.getValue());
lexer.takeComments(num);
num.setStart(lexer.getLastLocationAWS().copy());
num.setEnd(endProperty != null ? endProperty.copy() : lexer.getLocation().copy());
return num;
case Open:
JsonObject obj = new JsonObject();
lexer.takeComments(obj);
obj.setStart(lexer.getLastLocationAWS().copy());
if (lexer.getType() == TokenType.Open) {
lexer.next();
lexer.getStates().push(new State("", true));
}
else
throw lexer.error("Unexpected content at start of JSON: "+lexer.getType().toString());
if (lexer.getType() != TokenType.Close) {
parseProperty();
readObject("$", obj, true);
}
obj.setEnd(endProperty != null ? endProperty.copy() : lexer.getLocation().copy());
return obj;
case OpenArray:
JsonArray arr = new JsonArray();
lexer.takeComments(arr);
arr.setStart(lexer.getLastLocationAWS().copy());
lexer.next();
lexer.getStates().push(new State("", false));
if (lexer.getType() != TokenType.CloseArray) {
parseProperty();
readArray("$", arr, true);
}
arr.setEnd(endProperty != null ? endProperty.copy() : lexer.getLocation().copy());
return arr;
case String:
JsonString str = new JsonString(lexer.getValue());
lexer.takeComments(str);
str.setStart(lexer.getLastLocationAWS().copy());
str.setEnd(endProperty != null ? endProperty.copy() : lexer.getLocation().copy());
return str;
default:
}
throw lexer.error("Unexpected content at start of JSON: "+lexer.getType().toString());
}
private void readObject(String path, JsonObject obj, boolean root) throws IOException, JsonException {
while (!(itemType == ItemType.End) || (root && (itemType == ItemType.Eof))) {
switch (itemType) {
case Object:
JsonObject child = new JsonObject(); //(obj.path+'.'+ItemName);
child.setStart(startProperty.copy());
lexer.takeComments(child);
if (obj.has(itemName) && !allowDuplicates)
throw lexer.error("Duplicated property name: "+itemName+ " @ "+path);
obj.addForParser(itemName, child, itemNoComma, itemUnquoted, valueUnquoted);
next();
readObject(path+"."+itemName, child, false);
child.setEnd(endProperty.copy());
break;
case Boolean :
JsonBoolean childB = new JsonBoolean(Boolean.valueOf(itemValue));
childB.setStart(startProperty.copy());
lexer.takeComments(childB);
if (obj.has(itemName) && !allowDuplicates)
throw lexer.error("Duplicated property name: "+itemName+ " @ "+path);
obj.addForParser(itemName, childB, itemNoComma, itemUnquoted, valueUnquoted);
childB.setEnd(endProperty.copy());
break;
case String:
JsonString childS = new JsonString(itemValue);
childS.setStart(startProperty.copy());
lexer.takeComments(childS);
if (obj.has(itemName) && !allowDuplicates)
throw lexer.error("Duplicated property name: "+itemName+ " @ "+path);
obj.addForParser(itemName, childS, itemNoComma, itemUnquoted, valueUnquoted);
childS.setEnd(endProperty.copy());
break;
case Number:
JsonNumber childN = new JsonNumber(itemValue);
childN.setStart(startProperty.copy());
lexer.takeComments(childN);
if (obj.has(itemName) && !allowDuplicates)
throw lexer.error("Duplicated property name: "+itemName+ " @ "+path);
obj.addForParser(itemName, childN, itemNoComma, itemUnquoted, valueUnquoted);
childN.setEnd(endProperty.copy());
break;
case Null:
JsonNull childn = new JsonNull();
childn.setStart(startProperty.copy());
lexer.takeComments(childn);
if (obj.has(itemName) && !allowDuplicates)
throw lexer.error("Duplicated property name: "+itemName+ " @ "+path);
obj.addForParser(itemName, childn, itemNoComma, itemUnquoted, valueUnquoted);
childn.setEnd(endProperty.copy());
break;
case Array:
JsonArray childA = new JsonArray(); // (obj.path+'.'+ItemName);
childA.setStart(startProperty.copy());
lexer.takeComments(childA);
if (obj.has(itemName) && !allowDuplicates)
throw lexer.error("Duplicated property name: "+itemName+ " @ "+path);
obj.addForParser(itemName, childA, itemNoComma, itemUnquoted, valueUnquoted);
next();
if (!readArray(path+"."+itemName, childA, false))
next(true);
if (childA.getEnd() == null) {
childA.setEnd(endProperty.copy());
}
break;
case Eof :
throw lexer.error("Unexpected End of File");
case End:
throw lexer.error("Unexpected End"); // Don't think we can get here
}
itemNoComma = false;
endProperty = lexer.getLocation().copy();
next();
}
}
private boolean readArray(String path, JsonArray arr, boolean root) throws IOException, JsonException {
boolean res = false;
while (!((itemType == ItemType.End) || (root && (itemType == ItemType.Eof)))) {
res = true;
switch (itemType) {
case Object:
JsonObject obj = new JsonObject(); // (arr.path+'['+inttostr(i)+']');
obj.setStart(startProperty.copy());
lexer.takeComments(obj);
arr.addForParser(obj, itemNoComma, valueUnquoted);
next();
readObject(path+"["+(arr.size()-1)+"]", obj, false);
obj.setEnd(endProperty.copy());
break;
case String:
JsonString s = new JsonString(itemValue);
s.setStart(startProperty.copy());
lexer.takeComments(s);
arr.addForParser(s, itemNoComma, valueUnquoted);
s.setEnd(endProperty.copy());
break;
case Number:
JsonNumber n = new JsonNumber(itemValue);
n.setStart(startProperty.copy());
lexer.takeComments(n);
arr.addForParser(n, itemNoComma, valueUnquoted);
n.setEnd(endProperty.copy());
break;
case Boolean:
JsonBoolean b = new JsonBoolean("true".equals(itemValue));
b.setStart(startProperty.copy());
lexer.takeComments(b);
arr.addForParser(b, itemNoComma, valueUnquoted);
b.setEnd(endProperty.copy());
break;
case Null :
JsonNull nn = new JsonNull();
nn.setStart(startProperty.copy());
lexer.takeComments(nn);
arr.addForParser(nn, itemNoComma, valueUnquoted);
nn.setEnd(endProperty.copy());
break;
case Array:
JsonArray child = new JsonArray(); // (arr.path+'['+inttostr(i)+']');
child.setStart(startProperty.copy());
lexer.takeComments(child);
arr.addForParser(child, itemNoComma, valueUnquoted);
next();
readArray(path+"["+(arr.size()-1)+"]", child, false);
child.setEnd(endProperty.copy());
break;
case Eof :
throw lexer.error("Unexpected End of File");
case End:
throw lexer.error("Can't get here");
}
itemNoComma = false;
arr.setEnd(lexer.getLocation().copy());
next();
}
return res;
}
private void next() throws IOException {
next(false);
}
private void next(boolean noPop) throws IOException {
switch (itemType) {
case Object :
lexer.consume(TokenType.Open);
lexer.getStates().push(new State(itemName, true));
if (lexer.getType() == TokenType.Close) {
itemType = ItemType.End;
lexer.next();
} else
parseProperty();
break;
case Null:
case String:
case Number:
case End:
case Boolean :
if (itemType == ItemType.End && !noPop)
lexer.getStates().pop();
if (lexer.getType() == TokenType.Comma) {
lexer.next();
parseProperty();
} else if (lexer.getType() == TokenType.Close) {
itemType = ItemType.End;
lexer.next();
} else if (lexer.getType() == TokenType.CloseArray) {
itemType = ItemType.End;
lexer.next();
} else if (lexer.getType() == TokenType.Eof) {
itemType = ItemType.Eof;
} else if (allowNoComma && (lexer.getType() == TokenType.String || (!lexer.getStates().peek().isProp()) && lexer.getType().isValueType())) {
itemNoComma = true;
parseProperty();
} else {
throw lexer.error("Unexpected JSON syntax");
}
break;
case Array :
lexer.next();
lexer.getStates().push(new State(itemName+"[]", false));
parseProperty();
break;
case Eof :
throw lexer.error("JSON Syntax Error - attempt to read past end of json stream");
default:
throw lexer.error("not done yet (a): "+itemType.toString());
}
}
private void parseProperty() throws IOException {
if (lexer.getStates().peek().isProp()) {
itemUnquoted = lexer.isUnquoted();
itemName = lexer.consume(TokenType.String);
itemValue = null;
lexer.consume(TokenType.Colon);
}
startProperty = lexer.getLastLocationAWS().copy();
endProperty = lexer.getLocation().copy();
valueUnquoted = lexer.isUnquoted();
switch (lexer.getType()) {
case Null :
itemType = ItemType.Null;
itemValue = lexer.getValue();
lexer.next();
break;
case String :
itemType = ItemType.String;
itemValue = lexer.getValue();
lexer.next();
break;
case Boolean :
itemType = ItemType.Boolean;
itemValue = lexer.getValue();
lexer.next();
break;
case Number :
itemType = ItemType.Number;
itemValue = lexer.getValue();
lexer.next();
break;
case Open :
itemType = ItemType.Object;
break;
case OpenArray :
itemType = ItemType.Array;
break;
case CloseArray :
itemType = ItemType.End;
break;
// case Close, , case Colon, case Comma, case OpenArray, !
default:
throw lexer.error("not done yet (b): "+lexer.getType().toString());
}
}
private String write(JsonElement element, boolean pretty) {
StringBuilder b = new StringBuilder();
if (pretty && element.hasComments()) {
writeComments(b, element.getComments(), 0);
}
write(b, element, pretty, 0);
if (pretty) {
b.append("\n");
}
return b.toString();
}
private void writeComments(StringBuilder b, List<String> comments, int indent) {
for (String s : comments) {
b.append("// ");
b.append(s);
b.append("\n");
b.append(Utilities.padLeft("", ' ', indent));
}
}
private void write(StringBuilder b, JsonElement e, boolean pretty, int indent) {
switch (e.elementType()) {
case ARRAY:
JsonArray arr = (JsonArray) e;
b.append("[");
boolean first = true;
boolean complex = arr.size() > 6; // arbitrary cut off
if (!complex) {
int length = 0;
for (JsonElement i : arr.getItems()) {
if (i instanceof JsonPrimitive) {
length = length + i.toString().length();
}
if (i.elementType() == JsonElementType.ARRAY || i.elementType() == JsonElementType.OBJECT
|| i.hasComments()) { // 20 is a somewhat arbitrary cut off
complex = true;
}
}
if (length > 60) {
complex = true;
}
}
for (JsonElement i : arr.getItems()) {
if (first) first = false; else b.append(pretty && !complex ? ", " : ",");
if (pretty && complex) {
b.append("\n");
b.append(Utilities.padLeft("", ' ', indent+2));
if (i.hasComments()) {
writeComments(b, i.getComments(), indent+2);
}
}
write(b, i, pretty && complex, indent+2);
}
if (pretty && complex) {
b.append("\n");
b.append(Utilities.padLeft("", ' ', indent));
}
b.append("]");
break;
case BOOLEAN:
b.append(((JsonBoolean) e).getValue());
break;
case NULL:
b.append(((JsonNull) e).getValue());
break;
case NUMBER:
b.append(((JsonNumber) e).getValue());
break;
case OBJECT:
b.append("{");
first = true;
for (JsonProperty p : ((JsonObject) e).getProperties()) {
if (first) first = false; else b.append(",");
if (pretty) {
b.append("\n");
b.append(Utilities.padLeft("", ' ', indent+2));
if (p.getValue().hasComments()) {
writeComments(b, p.getValue().getComments(), indent+2);
}
}
b.append("\"");
b.append(p.getName());
b.append(pretty ? "\" : " : "\":");
write(b, p.getValue(), pretty, indent+2);
}
if (pretty) {
b.append("\n");
b.append(Utilities.padLeft("", ' ', indent));
}
b.append("}");
break;
case STRING:
b.append("\"");
b.append(Utilities.escapeJson(((JsonString) e).getValue()));
b.append("\"");
break;
default:
throw new Error("Can't get here");
}
}
private static byte[] fetch(String source) throws IOException {
SimpleHTTPClient fetcher = new SimpleHTTPClient();
HTTPResult res = fetcher.get(source+"?nocache=" + System.currentTimeMillis());
res.checkThrowException();
return res.getContent();
}
}

View File

@ -0,0 +1,587 @@
package org.hl7.fhir.utilities.json;
import java.io.IOException;
import org.hl7.fhir.utilities.json.model.JsonArray;
import org.hl7.fhir.utilities.json.model.JsonBoolean;
import org.hl7.fhir.utilities.json.model.JsonElement;
import org.hl7.fhir.utilities.json.model.JsonElementType;
import org.hl7.fhir.utilities.json.model.JsonNull;
import org.hl7.fhir.utilities.json.model.JsonNumber;
import org.hl7.fhir.utilities.json.model.JsonObject;
import org.hl7.fhir.utilities.json.model.JsonProperty;
import org.hl7.fhir.utilities.json.model.JsonString;
import org.hl7.fhir.utilities.json.parser.JsonParser;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class JsonParserTests {
@Test
public void testComments1() throws IOException, JsonException {
Assertions.assertThrows(IOException.class, () -> JsonParser.parseObject("{\n // some comment \n \"n1\" : \"v1\"\n}\n", false));
}
@Test
public void testComments2() throws IOException, JsonException {
JsonObject obj = JsonParser.parseObject("{\n // some comment \n \"n1\" : \"v1\"\n}\n", true);
Assertions.assertEquals(0, obj.getComments().size());
JsonString c = obj.getStr("n1");
Assertions.assertEquals(1, c.getComments().size());
Assertions.assertEquals("some comment", c.getComments().get(0));
Assertions.assertEquals("{\"n1\":\"v1\"}", JsonParser.compose(obj, false));
Assertions.assertEquals("{\n // some comment\n \"n1\" : \"v1\"\n}\n", JsonParser.compose(obj, true));
}
@Test
public void testComments3() throws IOException, JsonException {
JsonObject obj = JsonParser.parseObject("// some comment\n{\n \"n1\" : \"v1\"\n}\n", true);
Assertions.assertEquals(1, obj.getComments().size());
Assertions.assertEquals("some comment", obj.getComments().get(0));
JsonString c = obj.getStr("n1");
Assertions.assertEquals(0, c.getComments().size());
Assertions.assertEquals("{\"n1\":\"v1\"}", JsonParser.compose(obj, false));
Assertions.assertEquals("// some comment\n{\n \"n1\" : \"v1\"\n}\n", JsonParser.compose(obj, true));
}
@Test
public void testEmpty1() throws IOException, JsonException {
JsonObject obj = JsonParser.parseObject("{}");
Assertions.assertEquals(1, obj.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(1, obj.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(1, obj.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(3, obj.getEnd().getCol(), "end col is wrong");
}
@Test
public void testEmpty2() throws IOException, JsonException {
JsonObject obj = JsonParser.parseObject("{\r\n}\r\n");
Assertions.assertEquals(1, obj.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(1, obj.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(2, obj.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(2, obj.getEnd().getCol(), "end col is wrong");
}
private void checkSubstring(String src, JsonElement obj, String tgt) {
String s = src.substring(obj.getStart().getCol()-1, obj.getEnd().getCol()-1);
Assertions.assertEquals(tgt, s);
}
@Test
public void testString1() throws IOException, JsonException {
String src = "{\"name\":\"value\"}";
JsonObject obj = JsonParser.parseObject(src);
Assertions.assertEquals(1, obj.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(1, obj.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(1, obj.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(17, obj.getEnd().getCol(), "end col is wrong");
Assertions.assertEquals(1, obj.getProperties().size(), "prop count is wrong");
checkSubstring(src, obj, src);
JsonProperty p = obj.getProperties().get(0);
Assertions.assertNotNull(p);
JsonElement e = obj.get("name");
Assertions.assertSame(p.getValue(), e);
JsonString j = (JsonString) e;
checkSubstring(src, j, "\"value\"");
String s = obj.str("name");
Assertions.assertEquals(j.getValue(), s, "string prop value");
Assertions.assertEquals(1, j.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(9, j.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(1, j.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(16, j.getEnd().getCol(), "end col is wrong");
}
@Test
public void testString2() throws IOException, JsonException {
JsonObject obj = JsonParser.parseObject("{\r\n \"name\" : \"value\"\r\n}\r\n");
Assertions.assertEquals(1, obj.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(1, obj.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(3, obj.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(2, obj.getEnd().getCol(), "end col is wrong");
Assertions.assertEquals(1, obj.getProperties().size(), "prop count is wrong");
JsonElement e = obj.get("name");
JsonString j = (JsonString) e;
String s = obj.str("name");
Assertions.assertEquals(j.getValue(), s, "string prop value");
Assertions.assertEquals(2, j.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(12, j.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(2, j.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(19, j.getEnd().getCol(), "end col is wrong");
}
@Test
public void testString3() throws IOException, JsonException {
String src = "{\"name\":\"value\",\"n1\":\"v1\"}";
JsonObject obj = JsonParser.parseObject(src);
Assertions.assertEquals(1, obj.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(1, obj.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(1, obj.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(27, obj.getEnd().getCol(), "end col is wrong");
Assertions.assertEquals(2, obj.getProperties().size(), "prop count is wrong");
checkSubstring(src, obj, src);
JsonProperty p = obj.getProperties().get(0);
Assertions.assertNotNull(p);
JsonElement e = obj.get("name");
Assertions.assertSame(p.getValue(), e);
JsonString j = (JsonString) e;
checkSubstring(src, j, "\"value\"");
String s = obj.str("name");
Assertions.assertEquals(j.getValue(), s, "string prop value");
Assertions.assertEquals(1, j.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(9, j.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(1, j.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(16, j.getEnd().getCol(), "end col is wrong");
p = obj.getProperties().get(1);
Assertions.assertNotNull(p);
e = obj.get("n1");
j = (JsonString) e;
Assertions.assertSame(p.getValue(), e);
checkSubstring(src, j, "\"v1\"");
s = obj.str("n1");
Assertions.assertEquals(j.getValue(), s, "string prop value");
Assertions.assertEquals(1, j.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(22, j.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(1, j.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(26, j.getEnd().getCol(), "end col is wrong");
}
@Test
public void testString4() throws IOException, JsonException {
JsonObject obj = JsonParser.parseObject("{\r\n \"name\" : \"value\"\r\n}\r\n");
Assertions.assertEquals(1, obj.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(1, obj.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(3, obj.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(2, obj.getEnd().getCol(), "end col is wrong");
Assertions.assertEquals(1, obj.getProperties().size(), "prop count is wrong");
JsonElement e = obj.get("name");
JsonString j = (JsonString) e;
String s = obj.str("name");
Assertions.assertEquals(j.getValue(), s, "string prop value");
Assertions.assertEquals(2, j.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(12, j.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(2, j.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(19, j.getEnd().getCol(), "end col is wrong");
}
@Test
public void testStringE1() throws IOException, JsonException {
String src = "{\"name\":\"\"}";
JsonObject obj = JsonParser.parseObject(src);
Assertions.assertEquals(1, obj.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(1, obj.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(1, obj.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(12, obj.getEnd().getCol(), "end col is wrong");
Assertions.assertEquals(1, obj.getProperties().size(), "prop count is wrong");
checkSubstring(src, obj, src);
JsonProperty p = obj.getProperties().get(0);
Assertions.assertNotNull(p);
JsonElement e = obj.get("name");
JsonString j = (JsonString) e;
checkSubstring(src, j, "\"\"");
String s = obj.str("name");
Assertions.assertEquals(j.getValue(), s, "string prop value");
Assertions.assertEquals(1, j.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(9, j.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(1, j.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(11, j.getEnd().getCol(), "end col is wrong");
}
@Test
public void testStringE2() throws IOException, JsonException {
JsonObject obj = JsonParser.parseObject("{\r\n \"name\" : \"\"\r\n}\r\n");
Assertions.assertEquals(1, obj.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(1, obj.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(3, obj.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(2, obj.getEnd().getCol(), "end col is wrong");
Assertions.assertEquals(1, obj.getProperties().size(), "prop count is wrong");
JsonElement e = obj.get("name");
JsonString j = (JsonString) e;
String s = obj.str("name");
Assertions.assertEquals(j.getValue(), s, "string prop value");
Assertions.assertEquals(2, j.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(12, j.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(2, j.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(14, j.getEnd().getCol(), "end col is wrong");
}
@Test
public void testInt1() throws IOException, JsonException {
String src = "{\"name\":1}";
JsonObject obj = JsonParser.parseObject(src);
Assertions.assertEquals(1, obj.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(1, obj.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(1, obj.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(11, obj.getEnd().getCol(), "end col is wrong");
Assertions.assertEquals(1, obj.getProperties().size(), "prop count is wrong");
checkSubstring(src, obj, src);
JsonElement e = obj.get("name");
JsonNumber j = (JsonNumber) e;
checkSubstring(src, j, "1");
String s = obj.str("name");
Assertions.assertEquals(j.getValue(), s, "string prop value");
Assertions.assertEquals(1, j.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(9, j.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(1, j.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(10, j.getEnd().getCol(), "end col is wrong");
}
@Test
public void testInt2() throws IOException, JsonException {
JsonObject obj = JsonParser.parseObject("{\r\n \"name\" : 1\r\n}\r\n");
Assertions.assertEquals(1, obj.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(1, obj.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(3, obj.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(2, obj.getEnd().getCol(), "end col is wrong");
Assertions.assertEquals(1, obj.getProperties().size(), "prop count is wrong");
JsonProperty p = obj.getProperties().get(0);
Assertions.assertNotNull(p);
JsonElement e = obj.get("name");
JsonNumber j = (JsonNumber) e;
String s = obj.str("name");
Assertions.assertEquals(j.getValue(), s, "string prop value");
Assertions.assertEquals(2, j.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(12, j.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(2, j.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(13, j.getEnd().getCol(), "end col is wrong");
}
@Test
public void testBool1() throws IOException, JsonException {
String src = "{\"name\":true}";
JsonObject obj = JsonParser.parseObject(src);
Assertions.assertEquals(1, obj.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(1, obj.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(1, obj.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(14, obj.getEnd().getCol(), "end col is wrong");
Assertions.assertEquals(1, obj.getProperties().size(), "prop count is wrong");
checkSubstring(src, obj, src);
JsonElement e = obj.get("name");
JsonBoolean j = (JsonBoolean) e;
checkSubstring(src, j, "true");
String s = obj.str("name");
Assertions.assertEquals(j.getValue(), s, "string prop value");
Assertions.assertEquals(1, j.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(9, j.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(1, j.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(13, j.getEnd().getCol(), "end col is wrong");
}
@Test
public void testBool2() throws IOException, JsonException {
JsonObject obj = JsonParser.parseObject("{\r\n \"name\" : false\r\n}\r\n");
Assertions.assertEquals(1, obj.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(1, obj.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(3, obj.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(2, obj.getEnd().getCol(), "end col is wrong");
Assertions.assertEquals(1, obj.getProperties().size(), "prop count is wrong");
JsonProperty p = obj.getProperties().get(0);
Assertions.assertNotNull(p);
JsonElement e = obj.get("name");
JsonBoolean j = (JsonBoolean) e;
String s = obj.str("name");
Assertions.assertEquals(j.getValue(), s, "string prop value");
Assertions.assertEquals(2, j.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(12, j.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(2, j.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(17, j.getEnd().getCol(), "end col is wrong");
}
@Test
public void testNull1() throws IOException, JsonException {
String src = "{\"name\":null}";
JsonObject obj = JsonParser.parseObject(src);
Assertions.assertEquals(1, obj.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(1, obj.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(1, obj.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(14, obj.getEnd().getCol(), "end col is wrong");
Assertions.assertEquals(1, obj.getProperties().size(), "prop count is wrong");
checkSubstring(src, obj, src);
JsonElement e = obj.get("name");
JsonNull j = (JsonNull) e;
checkSubstring(src, j, "null");
String s = obj.str("name");
Assertions.assertEquals(j.getValue(), s, "string prop value");
Assertions.assertEquals(1, j.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(9, j.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(1, j.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(13, j.getEnd().getCol(), "end col is wrong");
}
@Test
public void testNull2() throws IOException, JsonException {
JsonObject obj = JsonParser.parseObject("{\r\n \"name\" : null\r\n}\r\n");
Assertions.assertEquals(1, obj.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(1, obj.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(3, obj.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(2, obj.getEnd().getCol(), "end col is wrong");
Assertions.assertEquals(1, obj.getProperties().size(), "prop count is wrong");
JsonProperty p = obj.getProperties().get(0);
Assertions.assertNotNull(p);
JsonElement e = obj.get("name");
JsonNull j = (JsonNull) e;
String s = obj.str("name");
Assertions.assertEquals(j.getValue(), s, "string prop value");
Assertions.assertEquals(2, j.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(12, j.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(2, j.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(16, j.getEnd().getCol(), "end col is wrong");
}
@Test
public void testObject1() throws IOException, JsonException {
String src = "{\"name\":{\"n1\":\"v1\"}}";
JsonObject obj = JsonParser.parseObject(src);
Assertions.assertEquals(1, obj.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(1, obj.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(1, obj.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(21, obj.getEnd().getCol(), "end col is wrong");
Assertions.assertEquals(1, obj.getProperties().size(), "prop count is wrong");
checkSubstring(src, obj, src);
JsonElement e = obj.get("name");
JsonObject j = (JsonObject) e;
checkSubstring(src, j, "{\"n1\":\"v1\"}");
Assertions.assertEquals(1, j.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(9, j.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(1, j.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(20, j.getEnd().getCol(), "end col is wrong");
JsonElement e1 = j.get("n1");
JsonString j1 = (JsonString) e1;
checkSubstring(src, j1, "\"v1\"");
Assertions.assertEquals("v1", j1.getValue());
Assertions.assertEquals(1, j1.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(15, j1.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(1, j1.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(19, j1.getEnd().getCol(), "end col is wrong");
}
@Test
public void testObject2() throws IOException, JsonException {
String src = "{\r\n \"name\" : {\r\n \"n1\":\"v1\"\r\n }\r\n}\r\n";
JsonObject obj = JsonParser.parseObject(src);
Assertions.assertEquals(1, obj.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(1, obj.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(5, obj.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(2, obj.getEnd().getCol(), "end col is wrong");
Assertions.assertEquals(1, obj.getProperties().size(), "prop count is wrong");
JsonElement e = obj.get("name");
JsonObject j = (JsonObject) e;
Assertions.assertEquals(2, j.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(12, j.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(4, j.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(4, j.getEnd().getCol(), "end col is wrong");
JsonElement e1 = j.get("n1");
JsonString j1 = (JsonString) e1;
Assertions.assertEquals("v1", j1.getValue());
Assertions.assertEquals(3, j1.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(10, j1.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(3, j1.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(14, j1.getEnd().getCol(), "end col is wrong");
}
@Test
public void testArrEmpty1() throws IOException, JsonException {
JsonObject obj = JsonParser.parseObject("{\"name\":[]}");
Assertions.assertEquals(1, obj.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(1, obj.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(1, obj.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(12, obj.getEnd().getCol(), "end col is wrong");
Assertions.assertEquals(1, obj.getProperties().size(), "prop count is wrong");
JsonElement e = obj.get("name");
JsonArray j = (JsonArray) e;
Assertions.assertEquals(1, j.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(9, j.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(1, j.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(11, j.getEnd().getCol(), "end col is wrong");
}
@Test
public void testArrEmpty2() throws IOException, JsonException {
JsonObject obj = JsonParser.parseObject("{\r\n \"name\" : [ ]\r\n}\r\n");
Assertions.assertEquals(1, obj.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(1, obj.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(3, obj.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(2, obj.getEnd().getCol(), "end col is wrong");
JsonElement e = obj.get("name");
JsonArray j = (JsonArray) e;
Assertions.assertEquals(2, j.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(12, j.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(2, j.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(15, j.getEnd().getCol(), "end col is wrong");
}
@Test
public void testArr() throws IOException, JsonException {
JsonObject obj = JsonParser.parseObject("{\"name\":[\"v\",{\"n\":\"v1\"}]}");
Assertions.assertEquals(1, obj.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(1, obj.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(1, obj.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(26, obj.getEnd().getCol(), "end col is wrong");
Assertions.assertEquals(1, obj.getProperties().size(), "prop count is wrong");
JsonElement e = obj.get("name");
JsonArray j = (JsonArray) e;
Assertions.assertEquals(1, j.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(9, j.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(1, j.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(25, j.getEnd().getCol(), "end col is wrong");
Assertions.assertEquals(2, j.size());
JsonElement e1 = j.getItems().get(0);
JsonString j1 = (JsonString) e1;
Assertions.assertEquals(1, j1.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(10, j1.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(1, j1.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(13, j1.getEnd().getCol(), "end col is wrong");
JsonElement e2 = j.getItems().get(1);
JsonObject j2 = (JsonObject) e2;
Assertions.assertEquals(1, j2.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(14, j2.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(1, j2.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(24, j2.getEnd().getCol(), "end col is wrong");
}
@Test
public void testArr2() throws IOException, JsonException {
JsonObject obj = JsonParser.parseObject("{\n \"name\" : [\n \"v\",\n {\n \"n\" : \"v1\"\n }\n ]\n}\n");
Assertions.assertEquals(1, obj.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(1, obj.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(8, obj.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(2, obj.getEnd().getCol(), "end col is wrong");
Assertions.assertEquals(1, obj.getProperties().size(), "prop count is wrong");
JsonElement e = obj.get("name");
JsonArray j = (JsonArray) e;
Assertions.assertEquals(2, j.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(12, j.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(7, j.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(4, j.getEnd().getCol(), "end col is wrong");
Assertions.assertEquals(2, j.size());
JsonElement e1 = j.getItems().get(0);
JsonString j1 = (JsonString) e1;
Assertions.assertEquals(3, j1.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(5, j1.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(3, j1.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(8, j1.getEnd().getCol(), "end col is wrong");
JsonElement e2 = j.getItems().get(1);
JsonObject j2 = (JsonObject) e2;
Assertions.assertEquals(4, j2.getStart().getLine(), "start line is wrong");
Assertions.assertEquals(5, j2.getStart().getCol(), "start col is wrong");
Assertions.assertEquals(6, j2.getEnd().getLine(), "end line is wrong");
Assertions.assertEquals(6, j2.getEnd().getCol(), "end col is wrong");
}
@Test
public void testElementBool() throws IOException, JsonException {
JsonElement e = JsonParser.parse("true");
Assertions.assertEquals(JsonElementType.BOOLEAN, e.elementType());
Assertions.assertEquals(true, ((JsonBoolean) e).isValue());
e = JsonParser.parse("// comment\nfalse", true);
Assertions.assertEquals(JsonElementType.BOOLEAN, e.elementType());
Assertions.assertEquals(false, ((JsonBoolean) e).isValue());
Assertions.assertEquals("false", JsonParser.compose(e));
Assertions.assertEquals("// comment\nfalse\n", JsonParser.compose(e, true));
}
@Test
public void testElementNumber() throws IOException, JsonException {
JsonElement e = JsonParser.parse("1");
Assertions.assertEquals(JsonElementType.NUMBER, e.elementType());
Assertions.assertEquals("1", ((JsonNumber) e).getValue());
e = JsonParser.parse("// comment \n-1.2e10", true);
Assertions.assertEquals(JsonElementType.NUMBER, e.elementType());
Assertions.assertEquals("-1.2e10", ((JsonNumber) e).getValue());
Assertions.assertEquals("-1.2e10", JsonParser.compose(e));
Assertions.assertEquals("// comment\n-1.2e10\n", JsonParser.compose(e, true));
}
@Test
public void testElementString() throws IOException, JsonException {
JsonElement e = JsonParser.parse("\"str\\ning\"");
Assertions.assertEquals(JsonElementType.STRING, e.elementType());
Assertions.assertEquals("str\ning", ((JsonString) e).getValue());
Assertions.assertEquals("\"str\\ning\"", JsonParser.compose(e));
Assertions.assertEquals("\"str\\ning\"\n", JsonParser.compose(e, true));
e = JsonParser.parse("// comment\n\"\"", true);
Assertions.assertEquals(JsonElementType.STRING, e.elementType());
Assertions.assertEquals("", ((JsonString) e).getValue());
}
@Test
public void testElementNull() throws IOException, JsonException {
JsonElement e = JsonParser.parse("null");
Assertions.assertEquals(JsonElementType.NULL, e.elementType());
Assertions.assertEquals("null", JsonParser.compose(e));
Assertions.assertEquals("null\n", JsonParser.compose(e, true));
}
@Test
public void testElementArray() throws IOException, JsonException {
JsonElement e = JsonParser.parse("[\"test\", null, true]");
Assertions.assertEquals(JsonElementType.ARRAY, e.elementType());
Assertions.assertEquals(3, ((JsonArray) e).size());
Assertions.assertEquals("[\"test\",null,true]", JsonParser.compose(e));
Assertions.assertEquals("[\"test\", null, true]\n", JsonParser.compose(e, true));
e = JsonParser.parse("// comment\n[]", true);
Assertions.assertEquals(JsonElementType.ARRAY, e.elementType());
Assertions.assertEquals(0, ((JsonArray) e).size());
}
@Test
public void testDuplicates1() throws IOException, JsonException {
Assertions.assertThrows(IOException.class, () -> JsonParser.parseObject("{ \"n\" : 1, \"n\" : 2 }", false, false));
}
@Test
public void testDuplicates2() throws IOException, JsonException {
JsonObject e = JsonParser.parseObject("{ \"n\" : 1, \"n\" : 2 }", false, true);
Assertions.assertEquals(2, e.getProperties().size());
Assertions.assertEquals(2, e.getInteger("n"));
Assertions.assertEquals("{\"n\":1,\"n\":2}", JsonParser.compose(e));
}
@Test
public void testNoComma1() throws IOException, JsonException {
Assertions.assertThrows(IOException.class, () -> JsonParser.parseObject("{ \"n1\" : 1 \"n2\" : 2 }", false));
}
@Test
public void testNoComma2() throws IOException, JsonException {
JsonObject e = JsonParser.parseObject("{ \"n1\" : 1 \"n2\" : 2 }", true);
Assertions.assertEquals(2, e.getProperties().size());
Assertions.assertEquals(false, e.getProperties().get(0).isNoComma());
Assertions.assertEquals(true, e.getProperties().get(1).isNoComma());
}
@Test
public void testNoCommaInArr1() throws IOException, JsonException {
Assertions.assertThrows(IOException.class, () -> JsonParser.parseObject("[1 2]", false));
}
@Test
public void testNoCommainArr2() throws IOException, JsonException {
JsonArray e = (JsonArray) JsonParser.parse("[1 2]", true);
Assertions.assertEquals(2, e.size());
Assertions.assertEquals(false, e.isNoComma(0));
Assertions.assertEquals(true, e.isNoComma(1));
}
@Test
public void testUnquoted1() throws IOException, JsonException {
Assertions.assertThrows(IOException.class, () -> JsonParser.parseObject("{ this: that, \"the\" : other}", false));
}
@Test
public void testUnquoted2() throws IOException, JsonException {
JsonObject e = JsonParser.parseObject("{ this: that, \"the\" : other}", true);
Assertions.assertEquals(2, e.getProperties().size());
Assertions.assertEquals(true, e.getProperties().get(0).isUnquotedName());
Assertions.assertEquals(true, e.getProperties().get(0).isUnquotedValue());
Assertions.assertEquals(false, e.getProperties().get(1).isUnquotedName());
Assertions.assertEquals(true, e.getProperties().get(1).isUnquotedValue());
}
}

View File

@ -5,7 +5,7 @@ import java.io.IOException;
import org.hl7.fhir.utilities.json.JsonTrackingParser;
import org.junit.jupiter.api.Test;
public class JsonParserTests {
public class JsonTrackingParserTests {
@Test
public void test() throws IOException {