Сообщение Re[8]: JSON vs BSON: очередное торжество больного воображени от 01.12.2022 21:47
Изменено 01.12.2022 21:48 vsb
Re[8]: JSON vs BSON: очередное торжество больного воображени
Вроде написал, на простых тестах работает. Как раз где-то в два часа уложился. Старался всё в точности по спеке делать. Баги если и есть, то исправляемые и размер не увеличат.
package test;
import java.io.IOException;
import java.io.Reader;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public sealed interface Json {
record Obj(Map<String, Json> members) implements Json {
}
record Arr(List<Json> elements) implements Json {
}
record Str(String value) implements Json {
}
record Num(BigDecimal value) implements Json {
}
record Bool(boolean value) implements Json {
}
record Null() implements Json {
}
static Obj parseObj(Reader reader) throws IOException {
skipWs(reader);
expectCh(reader, '{');
var members = new HashMap<String, Json>();
while (true) {
skipWs(reader);
if (peekCh(reader) == '}') {
expectCh(reader, '}');
return new Obj(Map.copyOf(members));
}
if (!members.isEmpty()) {
expectCh(reader, ',');
}
var str = parseStr(reader);
skipWs(reader);
expectCh(reader, ':');
var elem = parseValue(reader);
members.put(str.value(), elem);
}
}
static Arr parseArr(Reader reader) throws IOException {
skipWs(reader);
expectCh(reader, '[');
var elements = new ArrayList<Json>();
while (true) {
skipWs(reader);
if (peekCh(reader) == ']') {
expectCh(reader, ']');
return new Arr(List.copyOf(elements));
}
if (!elements.isEmpty()) {
expectCh(reader, ',');
}
var elem = parseValue(reader);
elements.add(elem);
}
}
static Json parseValue(Reader reader) throws IOException {
skipWs(reader);
char ch = peekCh(reader);
return switch (ch) {
case '"' -> parseStr(reader);
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> parseNum(reader);
case '{' -> parseObj(reader);
case '[' -> parseArr(reader);
case 't' -> {
expectCh(reader, 't');
expectCh(reader, 'r');
expectCh(reader, 'u');
expectCh(reader, 'e');
yield new Bool(true);
}
case 'f' -> {
expectCh(reader, 'f');
expectCh(reader, 'a');
expectCh(reader, 'l');
expectCh(reader, 's');
expectCh(reader, 'e');
yield new Bool(false);
}
case 'n' -> {
expectCh(reader, 'n');
expectCh(reader, 'u');
expectCh(reader, 'l');
expectCh(reader, 'l');
yield new Null();
}
default -> throw new RuntimeException("Unexpected character: '" + ch + '\'');
};
}
static Str parseStr(Reader reader) throws IOException {
skipWs(reader);
expectCh(reader, '"');
var sb = new StringBuilder();
while (true) {
char ch = readCh(reader);
if (ch == '"') {
return new Str(sb.toString());
}
if (ch != '\\') {
sb.append(ch);
continue;
}
ch = readCh(reader);
sb.append(switch (ch) {
case '"' -> '"';
case '\\' -> '\\';
case '/' -> '/';
case 'b' -> '\b';
case 'f' -> '\f';
case 'n' -> '\n';
case 'r' -> '\r';
case 't' -> '\t';
case 'u' -> {
char ch1 = readCh(reader);
char ch2 = readCh(reader);
char ch3 = readCh(reader);
char ch4 = readCh(reader);
String hex = new String(new char[] {ch1, ch2, ch3, ch4});
yield (char) Integer.parseInt(hex, 16);
}
default -> throw new RuntimeException("Unexpected character: " + ch);
});
}
}
static Num parseNum(Reader reader) throws IOException {
skipWs(reader);
var sb = new StringBuilder();
parseNumInteger(reader, sb);
char ch = peekCh(reader);
if (ch == '.') {
expectCh(reader, '.');
sb.append(ch);
parseNumDigits(reader, sb);
ch = peekCh(reader);
}
if (ch == 'e' || ch == 'E') {
expectCh(reader, ch);
sb.append(ch);
ch = peekCh(reader);
if (ch == '+' || ch == '-') {
expectCh(reader, ch);
sb.append(ch);
}
parseNumDigits(reader, sb);
}
return new Json.Num(new BigDecimal(sb.toString()));
}
private static void parseNumInteger(Reader reader, StringBuilder sb) throws IOException {
char ch = peekCh(reader);
if (ch == '-') {
expectCh(reader, ch);
sb.append(ch);
ch = peekCh(reader);
}
if (ch == '0') {
expectCh(reader, ch);
sb.append(ch);
return;
}
parseNumDigits(reader, sb);
}
private static void parseNumDigits(Reader reader, StringBuilder sb) throws IOException {
char ch = expectCh(reader, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
sb.append(ch);
ch = peekCh(reader);
while (ch >= '0' && ch <= '9') {
expectCh(reader, ch);
sb.append(ch);
ch = peekCh(reader);
}
}
private static char readCh(Reader reader) throws IOException {
int c = reader.read();
if (c == -1) {
throw new RuntimeException("Unexpected EOF");
}
return (char) c;
}
private static void skipWs(Reader reader) throws IOException {
char ch;
do {
reader.mark(1);
ch = readCh(reader);
} while (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t');
reader.reset();
}
private static char peekCh(Reader reader) throws IOException {
reader.mark(1);
char ch = readCh(reader);
reader.reset();
return ch;
}
private static char expectCh(Reader reader, char... echs) throws IOException {
char ch = readCh(reader);
for (char ech : echs) {
if (ch == ech) {
return ch;
}
}
if (echs.length == 0) {
throw new UnsupportedOperationException();
}
var sb = new StringBuilder("'").append(echs[0]).append("'");
for (int i = 1; i < echs.length; i++) {
sb.append('|').append("'").append(echs[i]).append("'");
}
throw new RuntimeException("Expected " + sb + " but got " + ch);
}
}
Re[8]: JSON vs BSON: очередное торжество больного воображени
Вроде написал, на простых тестах работает. Как раз где-то в два часа уложился. Старался всё в точности по спеке делать. Баги если и есть, то исправляемые и размер не увеличат.
код | |
| |