base.json.JsonParser Maven / Gradle / Ivy
/**
* Creative commons Attribution-NonCommercial license.
*
* http://creativecommons.org/licenses/by-nc/2.5/au/deed.en_GB
*
* NO WARRANTY IS GIVEN OR IMPLIED, USE AT YOUR OWN RISK.
*/
package base.json;
import java.io.BufferedReader;
import java.io.CharArrayReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
public class JsonParser {
private static final int MAX_TOKEN_LENGTH = 1024*200;
private List tokens;
private BufferedReader stream;
int tokenindex;
public JsonParser(String jsonString) throws IOException {
stream = new BufferedReader(new CharArrayReader(jsonString.toCharArray()));
tokens = new ArrayList();
}
public JsonParser(BufferedReader in) {
tokens = new ArrayList();
stream = in;
}
private Integer wasPutBack;
private int streamNext() throws IOException {
if(wasPutBack != null) {
int i = wasPutBack;
wasPutBack = null;
return i;
}
return stream.read();
}
private void streamPutBack(int i) {
if(wasPutBack != null) {
throw new IllegalStateException("Cant put back more than one character");
}
wasPutBack = i;
}
/**
* Reads a JSON array or dictionary.
*
* @return Either a List, Map, or NULL if a problem occured.
* @throws IOException Error occured reading stream.
*/
public Object parse() throws IOException {
tokenindex = 0;
String token = null;
while((token = getToken()) != null) {
tokens.add(token);
}
// If no tokens were found, null must be returned.
if(tokens.size() == 0) {
return null;
}
if(peekToken().equals("{")) {
return getDictionary();
}
if(peekToken().equals("[")) {
return getArray();
}
return null;
}
/**
* state table:
* 1 = no state
* 2 = string
* 3 = string escape sequence
* 4 = true, false or null
*/
private String getToken() throws IOException {
int tokenlength = 0;
char[] token = new char[MAX_TOKEN_LENGTH];
int state = 1;
while(true) {
int i = streamNext();
if(i == -1) {
if(tokenlength == 0) {
return null;
}
return String.copyValueOf(token, 0, tokenlength);
}
char c = (char)i;
switch(state) {
case 1 :
if(c == ' ' || c == '\t' || c == '\n' || c == '\r') {
continue;
}
if(c == '{' || c == '}' || c == '[' || c == ']' || c == ',' || c == ':') {
return ""+c;
}
if(c == '"') {
state = 2;
continue;
}
if(c == 't' || c == 'f' || c == 'n') {
state = 4;
token[tokenlength] = c;
tokenlength++;
continue;
}
if(c >= '0' && c <= '9') {
state = 5;
token[tokenlength] = c;
tokenlength++;
continue;
}
throw new IllegalArgumentException("Unexpected character: " + c);
case 2 :
if(c == '"') {
return String.copyValueOf(token, 0, tokenlength);
}
if(tokenlength == MAX_TOKEN_LENGTH) {
throw new IllegalArgumentException("Token exceeded maximum length of " + MAX_TOKEN_LENGTH + " characters.");
}
if(c == '\\') {
state = 3;
break;
}
token[tokenlength] = c;
tokenlength++;
break;
case 3 :
if(c == 'n') {
token[tokenlength] = '\n';
tokenlength++;
state = 2;
} else if(c == '"') {
token[tokenlength] = '"';
tokenlength++;
state = 2;
} else if(c == '\\') {
token[tokenlength] = '\\';
tokenlength++;
state = 2;
} else if(c == '/') {
token[tokenlength] = '/';
tokenlength++;
state = 2;
} else if(c == 'b') {
token[tokenlength] = '\b';
tokenlength++;
state = 2;
} else if(c == 'f') {
token[tokenlength] = '\f';
tokenlength++;
state = 2;
} else if(c == 'r') {
token[tokenlength] = '\r';
tokenlength++;
state = 2;
} else if(c == 't') {
token[tokenlength] = '\t';
tokenlength++;
state = 2;
} else {
token[tokenlength] = '\\';
tokenlength++;
if(tokenlength == MAX_TOKEN_LENGTH) {
throw new IllegalArgumentException("Token exceeded maximum length of " + MAX_TOKEN_LENGTH + " characters.");
}
token[tokenlength] = c;
tokenlength++;
state = 2;
}
break;
case 4 :
if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
token[tokenlength] = c;
tokenlength++;
}
String value = String.copyValueOf(token, 0, tokenlength).toLowerCase();
if(value.equals("true")) {
tokenlength = 0;
state = 0;
return "true";
}
if(value.equals("false")) {
tokenlength = 0;
state = 0;
return "false";
}
if(value.equals("null")) {
tokenlength = 0;
state = 0;
return "null";
}
if(value.length() >= 6) {
throw new IOException("Invalid JSON, unescaped text in JSON data: " + value);
}
break;
case 5 :
if(c >= '0' && c <= '9') {
token[tokenlength] = c;
tokenlength++;
} else {
streamPutBack(i);
String number = String.copyValueOf(token, 0, tokenlength).toLowerCase();
tokenlength = 0;
state = 0;
return number;
}
break;
default:
throw new IOException("Unexpected character in JSON file");
}
}
//TODO: huh?
//if(index!=string.length()) System.out.println("Unexpected character in JSON file");
//return null;
}
private Map getDictionary() {
if(!peekToken().equals("{")) {
throw new IllegalArgumentException("Expecting { but found " + peekToken());
}
nextToken();
Map dictionary = new Hashtable<>();
while(true) {
// Get name
String name = nextToken();
if(name == null) {
throw new IllegalArgumentException("Expecting name, but found nothing");
}
if(name.equals("}")) {
break;
}
// Get separator
String colon = nextToken();
if(colon == null) {
throw new IllegalArgumentException("Expecting colon, but found nothing");
}
if(!colon.equals(":")) {
throw new IllegalArgumentException("Expecting colon, but found nothing");
}
// Get value
String peek = peekToken();
if(peek.equals("[")) {
dictionary.put(name, getArray());
} else {
if(peek.equals("{")) {
dictionary.put(name, getDictionary());
} else {
dictionary.put(name, nextToken());
}
}
if(peekToken().equals(",")) {
nextToken();
} else if(!peekToken().equals("}")) {
throw new IllegalArgumentException("Expecting , or } but saw " + peekToken());
}
}
return dictionary;
}
private String nextToken() {
if(tokenindex == tokens.size()) {
return null;
}
String token = tokens.get(tokenindex);
tokenindex++;
return token;
}
private String peekToken() {
if(tokenindex >= tokens.size()) {
return null;
}
return tokens.get(tokenindex);
}
private List © 2015 - 2025 Weber Informatics LLC | Privacy Policy