New Json Parser
This commit is contained in:
parent
dbef068730
commit
011e2488a3
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package org.hl7.fhir.utilities.json;
|
||||
|
||||
public class JsonException extends Exception {
|
||||
|
||||
public JsonException(String msg) {
|
||||
// TODO Auto-generated constructor stub
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package org.hl7.fhir.utilities.json.model;
|
||||
|
||||
public class JsonComment {
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package org.hl7.fhir.utilities.json.model;
|
||||
|
||||
public enum JsonElementType {
|
||||
OBJECT, ARRAY, STRING, NUMBER, BOOLEAN, NULL;
|
||||
}
|
|
@ -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--;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package org.hl7.fhir.utilities.json.model;
|
||||
|
||||
public abstract class JsonPrimitive extends JsonElement {
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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 {
|
Loading…
Reference in New Issue