java - 将记录保存在 json 中附加它们并解析

标签 java json gson

我正在尝试使用 GSON 构建类似数据库的东西。我想存储同一对象的记录,并能够随时追加新记录。另外,我正在尝试构建解析器,它将所有对象从 json 转换为 ArrayList。我怎么能做到这一点?目前我写了这样的东西:

public void addRecord(Record record) throws IOException {

    JsonWriter writer = new JsonWriter(new FileWriter("/file.json", true));

    writer.setIndent(" ");
    gson.toJson(record, Record.class, writer);
    writer.close();
}

但是 intelliJ 告诉我 JSON 标准只允许一个顶级值。如何避免?据我所知,文件应该只包含一个列表,我应该向它附加新对象,但我不知道如何。另外,如何将它们解析为 ArrayList?

最佳答案

But intelliJ tells me that JSON standard allows only one top-level value.

是的。 您实现的是串联的 JSON(请参阅 more)。 您仍然可以使用它,但您的记录文件不会被 JSON 兼容的工具解析。 如果您不想使用“原始”JSON 流,您可以实现基于 JSON 数组的流。 这需要一些工作,但它可以让您生成和使用有效的 JSON 文档。

final class JsonStreams {

    private JsonStreams() {
    }

    @SuppressWarnings("resource")
    static void copy(final JsonReader reader, final JsonWriter writer)
            throws IOException {
        int level = 0;
        loop:
        for ( JsonToken token = reader.peek(); token != null; token = reader.peek() ) {
            switch ( token ) {
            case BEGIN_ARRAY:
                reader.beginArray();
                writer.beginArray();
                ++level;
                break;
            case END_ARRAY:
                reader.endArray();
                writer.endArray();
                if ( --level == 0 ) {
                    return;
                }
                break;
            case BEGIN_OBJECT:
                reader.beginObject();
                writer.beginObject();
                ++level;
                break;
            case END_OBJECT:
                reader.endObject();
                writer.endObject();
                if ( --level == 0 ) {
                    return;
                }
                break;
            case NAME:
                final String name = reader.nextName();
                writer.name(name);
                break;
            case STRING:
                final String s = reader.nextString();
                writer.value(s);
                break;
            case NUMBER:
                final Number n = new BigDecimal(reader.nextString());
                writer.value(n);
                break;
            case BOOLEAN:
                final boolean b = reader.nextBoolean();
                writer.value(b);
                break;
            case NULL:
                reader.nextNull();
                writer.nullValue();
                break;
            case END_DOCUMENT:
                break loop;
            default:
                throw new AssertionError(token);
            }
        }
    }

    static void appendToArray(final JsonReader jsonReader, final JsonWriter jsonWriter, final Consumer<? super JsonWriter> consumer)
            throws IOException {
        // Making JsonReader set to END_DOCUMENT if there is a blank/whitespace document
        try {
            jsonReader.hasNext();
        } catch ( final EOFException ignored ) {
        }
        // Checking the outer-most JSON token
        final JsonToken beginJsonToken = jsonReader.peek();
        switch ( beginJsonToken ) {
        // If it's a blank/whitespace document, then just write a single row
        case END_DOCUMENT:
            jsonWriter.beginArray();
            consumer.accept(jsonWriter);
            jsonWriter.endArray();
            break;
        // If the document starts with [, then unroll all its values
        case BEGIN_ARRAY:
            jsonReader.beginArray();
            jsonWriter.beginArray();
            final JsonToken endJsonToken = jsonReader.peek();
            if ( endJsonToken != JsonToken.END_ARRAY ) {
                // Copy all existing values
                while ( jsonReader.hasNext() ) {
                    final JsonToken rowJsonToken = jsonReader.peek();
                    switch ( rowJsonToken ) {
                    case BEGIN_ARRAY:
                    case BEGIN_OBJECT:
                    case STRING:
                    case NUMBER:
                    case BOOLEAN:
                    case NULL:
                        copy(jsonReader, jsonWriter);
                        break;
                    // The rest of tokens must never happen because we copy values
                    case END_ARRAY:
                    case END_OBJECT:
                    case NAME:
                    case END_DOCUMENT:
                    default:
                        throw new AssertionError(rowJsonToken);
                    }
                }
            }
            consumer.accept(jsonWriter);
            // End the document with ]
            jsonReader.endArray();
            jsonWriter.endArray();
            break;
        default:
            throw new JsonParseException("Unexpected outer token: " + beginJsonToken);
        }
    }

}
public static void main(final String... args)
        throws IOException {
    final Iterable<String> resourceNames = ImmutableList.of("1-blank.json", "2-empty.json", "3-some.json");
    for ( final String resourceName : resourceNames ) {
        try ( final JsonReader jsonReader = Resources.getPackageResourceJsonReader(Q50418170.class, resourceName) ) {
            final Writer writer = new StringWriter();
            final JsonWriter jsonWriter = new JsonWriter(writer);
            JsonStreams.appendToArray(jsonReader, jsonWriter, jw -> {
                gson.toJson(fooBar(1, 2), jw);
                gson.toJson(fooBar(3, 4), jw);
                gson.toJson(fooBar(5, 6), jw);
            });
            System.out.println(writer);
        }
    }
}

private static JsonElement fooBar(final int foo, final int bar) {
    final JsonObject jsonObject = new JsonObject();
    jsonObject.add("foo", new JsonPrimitive(foo));
    jsonObject.add("bar", new JsonPrimitive(bar));
    return jsonObject;
}

以上测试文件如下:

1-空白.json

<无内容;零长度文件>

2-empty.json

[
]

3-some.json

本文档是有意格式化的,但我会存储缩小的 JSON 文档(因此,我不会使用 setIndent)。

[
    {
        "foo": 0,
        "bar": 0,
        "baz": [
            1,
            2,
            3
        ]
    }
]

对上述文档的测试将产生以下输出

[{"foo":1,"bar":2},{"foo":3,"bar":4},{"foo":5,"bar":6}]
[{"foo":1,"bar":2},{"foo":3,"bar":4},{"foo":5,"bar":6}]
[{"foo":0,"bar":0,"baz":[1,2,3]},{"foo":1,"bar":2},{"foo":3,"bar":4},{"foo":5,"bar":6}]

关于java - 将记录保存在 json 中附加它们并解析,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50418170/

相关文章:

php - Recaptcha 请求返回 Null

javascript - 从 JSON 元素的内容构造 URL 以避免输出中出现编码字符的正确方法是什么?

java - Gson.json() 解析json文件时返回null

android - 文件还是数据库? - 在 Android 设备上保存对象的最佳实践

java - Java 的 if 语句中可以有 3 个条件语句吗?

java - 将多个 JFrame 中的值检索到单个 JFrame

.net - 在 .NET Core 中添加 Content-Type JSON header

jakarta-ee - 如何在 Java EE 7 的 JAX-RS 层中获取 Java 对象的 JSON 表示?

java - C++ "Object"类

java - HttpClient 无法在 Tomcat 上运行,NoSuchMethodError org/apache/http/entity/ContentType