io.github.endreman0.javajson.JavaJson Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java-json Show documentation
Show all versions of java-json Show documentation
A JSON parser and mutable object-model implememntation
package io.github.endreman0.javajson;
import io.github.endreman0.javajson.nodes.ArrayNode;
import io.github.endreman0.javajson.nodes.BooleanNode;
import io.github.endreman0.javajson.nodes.Node;
import io.github.endreman0.javajson.nodes.NullNode;
import io.github.endreman0.javajson.nodes.NumberNode;
import io.github.endreman0.javajson.nodes.ObjectNode;
import io.github.endreman0.javajson.nodes.StringNode;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.file.Files;
/**
* This class contains all of the methods to parse JSON.
* You can parse from {@link #parse(String) String}s, {@link #parse(File, Charset) File}s, and {@link #parse(InputStream) InputStream}s.
* @author endreman0
*/
public class JavaJson{
/**
* Parse a JSON node from a string.
*
* The passed string can be any JSON node: boolean, number, string, null, object, or array.
*
* If an invalid string is passed in, two things could happen:
*
* - If the first character of the string does not match any known JSON node, null will be returned.
* - If the first character matches a node, but there is an error somewhere else in the the string, an IllegalArgumentException will be thrown.
*
* @param json the string to parse
* @return the parsed node, or null if error
* @throws IllegalArgumentException if error
*/
public static Node parse(String json){
return parse(CharBuffer.wrap(json));
}
/**
* Parse a JSON node from a file.
* This is a wrapper for {@link #parse(String)}. It reads the file into a StringBuilder, then parses it with the aforementioned
* method. See that method's documentation for more information.
* @param file the file to parse
* @param charset the file's charset
* @return the parsed node, or null if error (see {@link #parse(String)})
* @throws IOException passed from the file reader
* @throws IllegalArgumentException if error (see {@link #parse(String)})
*/
public static Node parse(File file, Charset charset) throws IOException{
try(BufferedReader reader = Files.newBufferedReader(file.toPath(), charset)){
StringBuilder sb = new StringBuilder();
String line;
while((line = reader.readLine()) != null) sb.append(line).append("\r\n");
return parse(sb.toString());
}
}
/**
* Parse a JSON node from an InputStream.
* This is a wrapper for {@link #parse(String)}. It reads the stream into a StringBuilder, then parses it with the aforementioned
* method. See that method's documentation for more information.
* @param in the stream to parse
* @return the parsed node, or null if error (see {@link #parse(String)})
* @throws IOException passed from the file reader
* @throws IllegalArgumentException if error (see {@link #parse(String)})
*/
public static Node parse(InputStream in) throws IOException{
StringBuilder sb = new StringBuilder();
byte[] buffer = new byte[4096];
int read;
while(true){
read = in.read(buffer, 0, buffer.length);
if(read < buffer.length){
sb.append(new String(buffer).substring(0, read+1));
break;
}else{
sb.append(new String(buffer));
}
}
return parse(CharBuffer.wrap(sb.toString().toCharArray()));
}
protected static Node parse(CharBuffer json){
char start = json.get(json.position());
if(start == 't' || start == 'f') return parseBoolean(json);
else if(Character.isDigit(start)) return parseNumber(json);
else if(start == '\"') return parseString(json);
else if(start == '[') return parseArray(json);
else if(start == '{') return parseObject(json);
else if(start == 'n') return parseNull(json);
else return null;
}
protected static BooleanNode parseBoolean(CharBuffer json){
char first = json.get();
if(first == 't'){
if(json.get() == 'r' && json.get() == 'u' && json.get() == 'e') return new BooleanNode(true); else return null;
}else if(first == 'f'){
if(json.get() == 'a' && json.get() == 'l' && json.get() == 's' && json.get() == 'e') return new BooleanNode(false); else return null;
}else return null;
}
protected static NumberNode parseNumber(CharBuffer json){
StringBuffer data = new StringBuffer();
char next;
while(json.hasRemaining()){
next = json.get(json.position());
if(Character.isDigit(next) || next == '.'){//If the character is a number or decimal point,
data.append(next);//append it to the StringBuilder,
json.get();//and advance the buffer past it.
}else break;
}
return new NumberNode(Double.valueOf(data.toString()));
}
protected static StringNode parseString(CharBuffer json){
if(json.get() != '\"') return null;
StringBuffer data = new StringBuffer();
char next;
while((next = json.get()) != '\"') data.append(next);
return new StringNode(data.toString());
}
protected static ArrayNode parseArray(CharBuffer json){
if(json.get() != '[') return null;
ArrayNode node = new ArrayNode();
while(json.hasRemaining()){
trim(json);
node.add(parse(json));
trim(json);
if(json.get(json.position()) == ']'){
json.get();
break;
}
expect(json.get(), ',');
}
return node;
}
protected static ObjectNode parseObject(CharBuffer json){
expect(json.get(), '{');
ObjectNode node = new ObjectNode();
trim(json);
while(json.hasRemaining() && json.get(json.position()) != '}'){
expect(json.get(), '\"');//Leading quote of the key
String key = readFor(json, false, '\"');//Read until the end quote
trim(json);//Trim away any spaces or newlines
expect(json.get(), ':');
trim(json);
Node value = parse(json);//After the colon, the next item is the value for the previous key
node.put(key, value);
trim(json);
if(json.get(json.position()) == '}'){//End of the object is reached
json.get();//Advance past the bracket
break;//End parsing - it's the end of the object
}
expect(json.get(), ',');//If it's not the end, read the comma for the next field and read the next field.
trim(json);
}
return node;
}
protected static NullNode parseNull(CharBuffer json){
if(json.get() == 'n' && json.get() == 'u' && json.get() == 'l' && json.get() == 'l') return new NullNode(); else return null;
}
protected static String readFor(CharBuffer json, boolean trim, char... targets){
StringBuilder buffer = new StringBuilder();
char next;
loop: while(json.hasRemaining()){
next = json.get();
for(char target : targets) if(next == target) break loop;
if(trim && (next == ' ' || next == '\r' || next == '\n' || next == '\t')) ;//Don't copy spacers if trimming is enabled
else buffer.append(next);
}
return buffer.toString();
}
protected static void trim(CharBuffer json){
char next;
while(json.hasRemaining()){
next = json.get(json.position());
if(next == ' ' || next == '\r' || next == '\n' || next == '\t')json.get();//Clear the character out
else break;//Stop trimming once the end of the trimmable characters is reached
}
}
protected static void expect(char actual, char... expected){
boolean isExpected = false;//Matches an expected character
for(char c : expected)
if(actual == c) isExpected = true;
if(!isExpected){//Exception time!
StringBuffer message = new StringBuffer("Expected chars [");
for(int i=0;i