将 JSON 文件中的所有元素收集到一个列表中
Collect all elements in a JSON file into a single list
我正在使用 Gson 2.8.1+(如果需要我可以升级)。
如果我有 JsonObject:
"config" : {
"option_one" : {
"option_two" : "result_one"
}
}
}
...我怎样才能有效地将其转换为以下形式:
"config.option_one.option_two" : "result_one"
算法
你能想到的最简单的算法是递归折叠。您首先递归地潜入结构的底部,然后询问地图中是否只有一个元素(您必须使用某些框架解析 json 以获得 Map 结构)。如果存在,则将父字段的字符串与 属性 连接起来,并将父字段的值设置为 属性 的值。然后你向上移动并重复这个过程,直到你在根部。当然,如果map有多个字段,你就移到parent去试试egan。
简单示例:
public static void main(String[] args) {
String str = """
{
"config" : {
"option_one" : {
"option_two" : "result_one"
}
}
}""";
var obj = JsonParser.parseString(str).getAsJsonObject();
System.out.println(flatten(obj)); // {"config.option_one.option_two":"result_one"}
}
public static JsonObject flatten(JsonObject toFlatten) {
var flattened = new JsonObject();
flatten0("", toFlatten, flattened);
return flattened;
}
private static void flatten0(String prefix, JsonObject toFlatten, JsonObject toMutate) {
for (var entry : toFlatten.entrySet()) {
var keyWithPrefix = prefix + entry.getKey();
if (entry.getValue() instanceof JsonObject child) {
flatten0(keyWithPrefix + ".", child, toMutate);
} else {
toMutate.add(keyWithPrefix, entry.getValue());
}
}
}
Gson 没有类似的东西,但它提供了足够的能力来构建它:你可以遍历 JSON 流(JsonReader
)和树(JsonElement
,但是没有包装成 JsonReader
) 基于堆栈和 stack-based/recursively 相应地(流可能会节省很多)。
我会创建一个通用的树行走方法来适应它以用于进一步的目的。
public static void walk(final JsonElement jsonElement, final BiConsumer<? super Collection<?>, ? super JsonElement> consumer) {
final Deque<Object> parents = new ArrayDeque<>();
parents.push("$");
walk(jsonElement, consumer, parents);
}
private static void walk(final JsonElement jsonElement, final BiConsumer<? super Collection<?>, ? super JsonElement> consumer, final Deque<Object> path) {
if ( jsonElement.isJsonNull() ) {
consumer.accept(path, jsonElement);
} else if ( jsonElement.isJsonPrimitive() ) {
consumer.accept(path, jsonElement);
} else if ( jsonElement.isJsonObject() ) {
for ( final Map.Entry<String, JsonElement> e : jsonElement.getAsJsonObject().entrySet() ) {
path.addLast(e.getKey());
walk(e.getValue(), consumer, path);
path.removeLast();
}
} else if ( jsonElement.isJsonArray() ) {
int i = 0;
for ( final JsonElement e : jsonElement.getAsJsonArray() ) {
path.addLast(i++);
walk(e, consumer, path);
path.removeLast();
}
} else {
throw new AssertionError(jsonElement);
}
}
注意上面的方法也支持数组。 walk
方法是推送语义驱动的:它使用回调来提供步行进度。通过返回迭代器或流使其变得惰性可能会更便宜并应用拉动语义。此外,CharSequence
视图元素可能会节省创建许多字符串的时间。
public static String toJsonPath(final Iterable<?> path) {
final StringBuilder stringBuilder = new StringBuilder();
final Iterator<?> iterator = path.iterator();
if ( iterator.hasNext() ) {
final Object next = iterator.next();
stringBuilder.append(next);
}
while ( iterator.hasNext() ) {
final Object next = iterator.next();
if ( next instanceof Number ) {
stringBuilder.append('[').append(next).append(']');
} else if ( next instanceof CharSequence ) {
stringBuilder.append('.').append(next);
} else {
throw new UnsupportedOperationException("Unsupported: " + next);
}
}
return stringBuilder.toString();
}
测试:
final JsonElement jsonElement = Streams.parse(jsonReader);
final Collection<String> paths = new ArrayList<>();
JsonPaths.walk(jsonElement, (path, element) -> paths.add(JsonPaths.toJsonPath(path)));
for ( final String path : paths ) {
System.out.println(path);
}
Assertions.assertIterableEquals(
ImmutableList.of(
"$.nothing",
"$.number",
"$.object.subobject.number",
"$.array[0].string",
"$.array[1].string",
"$.array[2][0][0][0]"
),
paths
);
我正在使用 Gson 2.8.1+(如果需要我可以升级)。
如果我有 JsonObject:
"config" : {
"option_one" : {
"option_two" : "result_one"
}
}
}
...我怎样才能有效地将其转换为以下形式:
"config.option_one.option_two" : "result_one"
算法
你能想到的最简单的算法是递归折叠。您首先递归地潜入结构的底部,然后询问地图中是否只有一个元素(您必须使用某些框架解析 json 以获得 Map
简单示例:
public static void main(String[] args) {
String str = """
{
"config" : {
"option_one" : {
"option_two" : "result_one"
}
}
}""";
var obj = JsonParser.parseString(str).getAsJsonObject();
System.out.println(flatten(obj)); // {"config.option_one.option_two":"result_one"}
}
public static JsonObject flatten(JsonObject toFlatten) {
var flattened = new JsonObject();
flatten0("", toFlatten, flattened);
return flattened;
}
private static void flatten0(String prefix, JsonObject toFlatten, JsonObject toMutate) {
for (var entry : toFlatten.entrySet()) {
var keyWithPrefix = prefix + entry.getKey();
if (entry.getValue() instanceof JsonObject child) {
flatten0(keyWithPrefix + ".", child, toMutate);
} else {
toMutate.add(keyWithPrefix, entry.getValue());
}
}
}
Gson 没有类似的东西,但它提供了足够的能力来构建它:你可以遍历 JSON 流(JsonReader
)和树(JsonElement
,但是没有包装成 JsonReader
) 基于堆栈和 stack-based/recursively 相应地(流可能会节省很多)。
我会创建一个通用的树行走方法来适应它以用于进一步的目的。
public static void walk(final JsonElement jsonElement, final BiConsumer<? super Collection<?>, ? super JsonElement> consumer) {
final Deque<Object> parents = new ArrayDeque<>();
parents.push("$");
walk(jsonElement, consumer, parents);
}
private static void walk(final JsonElement jsonElement, final BiConsumer<? super Collection<?>, ? super JsonElement> consumer, final Deque<Object> path) {
if ( jsonElement.isJsonNull() ) {
consumer.accept(path, jsonElement);
} else if ( jsonElement.isJsonPrimitive() ) {
consumer.accept(path, jsonElement);
} else if ( jsonElement.isJsonObject() ) {
for ( final Map.Entry<String, JsonElement> e : jsonElement.getAsJsonObject().entrySet() ) {
path.addLast(e.getKey());
walk(e.getValue(), consumer, path);
path.removeLast();
}
} else if ( jsonElement.isJsonArray() ) {
int i = 0;
for ( final JsonElement e : jsonElement.getAsJsonArray() ) {
path.addLast(i++);
walk(e, consumer, path);
path.removeLast();
}
} else {
throw new AssertionError(jsonElement);
}
}
注意上面的方法也支持数组。 walk
方法是推送语义驱动的:它使用回调来提供步行进度。通过返回迭代器或流使其变得惰性可能会更便宜并应用拉动语义。此外,CharSequence
视图元素可能会节省创建许多字符串的时间。
public static String toJsonPath(final Iterable<?> path) {
final StringBuilder stringBuilder = new StringBuilder();
final Iterator<?> iterator = path.iterator();
if ( iterator.hasNext() ) {
final Object next = iterator.next();
stringBuilder.append(next);
}
while ( iterator.hasNext() ) {
final Object next = iterator.next();
if ( next instanceof Number ) {
stringBuilder.append('[').append(next).append(']');
} else if ( next instanceof CharSequence ) {
stringBuilder.append('.').append(next);
} else {
throw new UnsupportedOperationException("Unsupported: " + next);
}
}
return stringBuilder.toString();
}
测试:
final JsonElement jsonElement = Streams.parse(jsonReader);
final Collection<String> paths = new ArrayList<>();
JsonPaths.walk(jsonElement, (path, element) -> paths.add(JsonPaths.toJsonPath(path)));
for ( final String path : paths ) {
System.out.println(path);
}
Assertions.assertIterableEquals(
ImmutableList.of(
"$.nothing",
"$.number",
"$.object.subobject.number",
"$.array[0].string",
"$.array[1].string",
"$.array[2][0][0][0]"
),
paths
);