io.github.kits.json.tokenizer.JsonTokenizer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of whimthen-kits Show documentation
Show all versions of whimthen-kits Show documentation
Easy to use java tool library.
The newest version!
package io.github.kits.json.tokenizer;
import io.github.kits.StringKit;
import io.github.kits.exception.JsonParseException;
import java.io.IOException;
import static io.github.kits.json.tokenizer.JsonTokenType.BEGIN_ARRAY;
import static io.github.kits.json.tokenizer.JsonTokenType.BEGIN_OBJECT;
import static io.github.kits.json.tokenizer.JsonTokenType.BOOLEAN;
import static io.github.kits.json.tokenizer.JsonTokenType.END_ARRAY;
import static io.github.kits.json.tokenizer.JsonTokenType.END_DOCUMENT;
import static io.github.kits.json.tokenizer.JsonTokenType.END_OBJECT;
import static io.github.kits.json.tokenizer.JsonTokenType.NULL;
import static io.github.kits.json.tokenizer.JsonTokenType.NUMBER;
import static io.github.kits.json.tokenizer.JsonTokenType.SEP_COLON;
import static io.github.kits.json.tokenizer.JsonTokenType.SEP_COMMA;
import static io.github.kits.json.tokenizer.JsonTokenType.STRING;
/**
* @project: kits
* @created: with IDEA
* @author: whimthen
* @date: 2019-03-05-11:18 | March. Tuesday
*/
public class JsonTokenizer {
private CharReader charReader;
private JsonTokenList jsonTokens;
/**
* Parse a JSON string into a JsonTokenList
*
* @param json JSON String
* @return JsonTokenList
* @throws IOException IOException
*/
public JsonTokenList tokenize(String json) throws IOException {
if (StringKit.isNullOrEmpty(json)) {
throw new JsonParseException("Json string is null or empty!");
}
this.charReader = new CharReader(json);
this.jsonTokens = new JsonTokenList();
jsonTokenize();
return jsonTokens;
}
/**
* Parse characters into JsonToken
*
* @throws IOException IOException
*/
private void jsonTokenize() throws IOException {
// 使用do-while处理空文件
JsonToken jsonToken;
do {
jsonToken = start();
jsonTokens.add(jsonToken);
} while (jsonToken.getTokenType() != END_DOCUMENT);
}
/**
* Parse and load each character in CharReader to the appropriate type
*
* @return JsonToken
* @throws IOException IOException
*/
private JsonToken start() throws IOException {
char ch;
do {
if (!charReader.hasMore()) {
return new JsonToken(END_DOCUMENT, null);
}
ch = charReader.next();
} while (isWhiteSpace(ch));
switch (ch) {
case '{':
return new JsonToken(BEGIN_OBJECT, String.valueOf(ch));
case '}':
return new JsonToken(END_OBJECT, String.valueOf(ch));
case '[':
return new JsonToken(BEGIN_ARRAY, String.valueOf(ch));
case ']':
return new JsonToken(END_ARRAY, String.valueOf(ch));
case ',':
return new JsonToken(SEP_COMMA, String.valueOf(ch));
case ':':
return new JsonToken(SEP_COLON, String.valueOf(ch));
case 'n':
return readNull();
case 't':
case 'f':
return readBoolean();
case '"':
return readString();
case '-':
return readNumber();
}
if (isDigitOne2Nine(ch)) {
return readNumber();
}
throw new JsonParseException("Illegal character");
}
/**
* Handling spaces or line breaks, etc.
*
* @param ch Char
* @return true-is space | false-not space
*/
private boolean isWhiteSpace(char ch) {
return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
}
/**
* Read the character as a JsonToken of type String
*
* @return JsonToken
* @throws IOException IOException
*/
private JsonToken readString() throws IOException {
StringBuilder sb = new StringBuilder();
for (;;) {
char ch = charReader.next();
if (ch == '\\') {
if (!isEscape()) {
throw new JsonParseException("Invalid escape character");
}
sb.append('\\');
ch = charReader.peek();
sb.append(ch);
if (ch == 'u') {
for (int i = 0; i < 4; i++) {
ch = charReader.next();
if (isHex(ch)) {
sb.append(ch);
} else {
throw new JsonParseException("Invalid character");
}
}
}
} else if (ch == '"') {
return new JsonToken(STRING, sb.toString());
} else if (ch == '\r' || ch == '\n') {
throw new JsonParseException("Invalid character");
} else {
sb.append(ch);
}
}
}
/**
* Determine if it is a special character
*
* @return true-it's special | false-not special
* @throws IOException IOException
*/
private boolean isEscape() throws IOException {
char ch = charReader.next();
return (ch == '"' || ch == '\\' || ch == 'u' || ch == 'r'
|| ch == 'n' || ch == 'b' || ch == 't' || ch == 'f');
}
/**
* Determine if it is a hex number
*
* @param ch Char
* @return true-is hex | false-not hex
*/
private boolean isHex(char ch) {
return ((ch >= '0' && ch <= '9') || ('a' <= ch && ch <= 'f')
|| ('A' <= ch && ch <= 'F'));
}
/**
* Read characters as a numeric type of JsonToken
*
* @return JsonToken
* @throws IOException IOException
*/
private JsonToken readNumber() throws IOException {
char ch = charReader.peek();
StringBuilder sb = new StringBuilder();
if (ch == '-') { // 处理负数
sb.append(ch);
ch = charReader.next();
if (ch == '0') { // 处理 -0.xxxx
sb.append(ch);
sb.append(readFracAndExp());
} else if (isDigitOne2Nine(ch)) {
addDigit(ch, sb);
} else {
throw new JsonParseException("Invalid minus number");
}
} else if (ch == '0') { // 处理小数
sb.append(ch);
sb.append(readFracAndExp());
} else {
addDigit(ch, sb);
}
return new JsonToken(NUMBER, sb.toString());
}
private void addDigit(char ch, StringBuilder sb) throws IOException {
do {
sb.append(ch);
ch = charReader.next();
} while (isDigitOne2Nine(ch));
if (ch != (char) -1) {
charReader.back();
sb.append(readFracAndExp());
}
}
/**
* Determine whether it is a scientific notation
*
* @param ch Char
* @return true-scientific | false-not scientific
*/
private boolean isExp(char ch) {
return ch == 'e' || ch == 'E';
}
/**
* Determine if it is a number for 0-9
*
* @param ch Char
* @return true-number | false-not number
*/
private boolean isDigitOne2Nine(char ch) {
return ch >= '0' && ch <= '9';
}
/**
* Reading floating number as String
*
* @return String
* @throws IOException IOException
*/
private String readFracAndExp() throws IOException {
StringBuilder sb = new StringBuilder();
char ch = charReader.next();
if (ch == '.') {
sb.append(ch);
ch = charReader.next();
if (!isDigitOne2Nine(ch)) {
throw new JsonParseException("Invalid frac");
}
do {
sb.append(ch);
ch = charReader.next();
} while (isDigitOne2Nine(ch));
if (isExp(ch)) { // 处理科学计数法
sb.append(ch);
sb.append(readExp());
} else {
if (ch != (char) -1) {
charReader.back();
}
}
} else if (isExp(ch)) {
sb.append(ch);
sb.append(readExp());
} else {
charReader.back();
}
return sb.toString();
}
/**
* Reading scientific notation
*
* @return String
* @throws IOException IOException
*/
private String readExp() throws IOException {
StringBuilder sb = new StringBuilder();
char ch = charReader.next();
if (ch == '+' || ch =='-') {
sb.append(ch);
ch = charReader.next();
if (isDigitOne2Nine(ch)) {
do {
sb.append(ch);
ch = charReader.next();
} while (isDigitOne2Nine(ch));
if (ch != (char) -1) { // 读取结束,不用回退
charReader.back();
}
} else {
throw new JsonParseException("e or E");
}
} else {
throw new JsonParseException("e or E");
}
return sb.toString();
}
/**
* Reading boolean value as JsonToken
*
* @return JsonToken
* @throws IOException IOException
*/
private JsonToken readBoolean() throws IOException {
if (charReader.peek() == 't') {
if (!(charReader.next() == 'r' && charReader.next() == 'u' && charReader.next() == 'e')) {
throw new JsonParseException("Invalid json string");
}
return new JsonToken(BOOLEAN, "true");
} else {
if (!(charReader.next() == 'a' && charReader.next() == 'l'
&& charReader.next() == 's' && charReader.next() == 'e')) {
throw new JsonParseException("Invalid json string");
}
return new JsonToken(BOOLEAN, "false");
}
}
/**
* Reading null value as JsonToken
*
* @return JsonToken
* @throws IOException IOException
*/
private JsonToken readNull() throws IOException {
if (!(charReader.next() == 'u' && charReader.next() == 'l' && charReader.next() == 'l')) {
throw new JsonParseException("Invalid json string");
}
return new JsonToken(NULL, "null");
}
}