Skip to content

Instantly share code, notes, and snippets.

@komamitsu
Last active August 23, 2021 06:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save komamitsu/a44835879b04829892f9c56db94d1f45 to your computer and use it in GitHub Desktop.
Save komamitsu/a44835879b04829892f9c56db94d1f45 to your computer and use it in GitHub Desktop.
import java.util.*;
class JsonParser {
private Object parsePrimitive(StringTokenizer tokenizer, String token) {
if (token.startsWith("\"")) {
if (token.endsWith("\"")) {
return token.subSequence(1, token.length() - 1);
}
StringBuilder builder = new StringBuilder(token);
while (tokenizer.hasMoreTokens()) {
token = tokenizer.nextToken();
if (token.endsWith("\"")) {
return builder.append(token).subSequence(1, builder.length() - 1).toString();
}
builder.append(token);
}
throw new RuntimeException(String.format("Invalid String: %s", builder));
}
else {
try {
return Long.parseLong(token);
}
catch (NumberFormatException e) {
return Double.parseDouble(token);
}
}
}
private String parseKey(StringTokenizer tokenizer, String firstToken) {
Object primitive = parsePrimitive(tokenizer, firstToken);
if (primitive instanceof String) {
return (String) primitive;
}
throw new RuntimeException(String.format("key should be String: %s", primitive));
}
private void invalidStateAndInput(ObjectParseState state, String token) {
throw new RuntimeException(String.format("Unexpected input. state: %s, input: %s", state, token));
}
private Object parseValue(StringTokenizer tokenizer, String token) {
if (token.equals("{")) {
return parseObject(tokenizer);
}
else if (token.equals("[")) {
return parseArray(tokenizer);
}
else {
return parsePrimitive(tokenizer, token);
}
}
private Map<String, Object> parseObject(StringTokenizer tokenizer) {
ObjectParseState state = ObjectParseState.PARSE_KEY;
Map<String, Object> obj = new HashMap<>();
String key = null;
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken().trim();
if (token.isEmpty()) {
continue;
}
switch (state) {
case PARSE_KEY:
if (token.equals("}")) {
// Empty obj
return obj;
}
key = parseKey(tokenizer, token);
state = ObjectParseState.PARSE_COLON;
break;
case PARSE_COLON:
if (token.equals(":")) {
state = ObjectParseState.PARSE_VALUE;
}
else {
invalidStateAndInput(state, token);
}
break;
case PARSE_VALUE:
obj.put(key, parseValue(tokenizer, token));
state = ObjectParseState.PARSE_COMMA;
break;
case PARSE_COMMA:
if (token.equals("}")) {
return obj;
}
else if (token.equals(",")) {
state = ObjectParseState.PARSE_KEY;
}
else {
invalidStateAndInput(state, token);
}
break;
default:
invalidStateAndInput(state, token);
break;
}
}
throw new RuntimeException("Incomplete object");
}
private List<Object> parseArray(StringTokenizer tokenizer) {
List<Object> array = new ArrayList<>();
boolean waitValue = true;
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken().trim();
if (token.isEmpty()) {
continue;
}
if (waitValue) {
array.add(parseValue(tokenizer, token));
waitValue = false;
}
else {
if (token.equals("]")) {
return array;
}
else if (token.equals(",")) {
waitValue = true;
}
else {
throw new RuntimeException("A comma is missing in an array");
}
}
}
throw new RuntimeException("Incomplete array");
}
Object parse(String s) {
StringTokenizer tokenizer = new StringTokenizer(s, "{:},", true);
if (tokenizer.hasMoreTokens()) {
return parseValue(tokenizer, tokenizer.nextToken());
}
else {
return null;
}
}
private enum ObjectParseState {
PARSE_KEY,
PARSE_COLON,
PARSE_VALUE,
PARSE_COMMA
}
public static void main(String[] args) {
System.out.println(new JsonParser().parse(
"{ \"group name\" : \"development\",\n" +
" \"members\": [\n" +
" {\n" +
" \"name\":\"Alice\",\n" +
" \"age\":42,\n" +
" \"language\":\"Zig\"\n" +
" }, {\n" +
" \"name\" : \"Bob\" , \"age\" : 16 ,\n" +
" \"language\" : \"Perl\" ,\n" +
" \"pi\" : 3.14\n" +
" }\n" +
" ]\n" +
"}"
));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment