在不反序列化的情况下从 JSON 字符串中的任何位置删除键

Remove keys from anywhere in a JSON string without deserialization

如果不反序列化和重新序列化以下深层嵌套的 JSON 字符串(如果可能的话),如何从任何嵌套级别删除名称为 "REMOVEME" 的任何键?

尝试过:

不情愿地尝试反序列化到JsonObject/LinkedHashMap以遍历带有node.remove(e.getKey())的结构在违规键上,但Gson抛出并发修改异常,Jackson要求完全反序列化。

之前:

{
    "a": {
        "REMOVEME" : "unwanted",
        "b": {
            "REMOVEME" : "unwanted",
            "c": {
                "d": {
                    "REMOVEME" : "unwanted",
                    "something": "default",
                    "e": {
                        "f": {
                            "REMOVEME" : "unwanted",
                            "g": {
                                "REMOVEME" : "unwanted",        
                                "h": {
                                    ... ,

之后:

{
    "a": {
        "b": {
            "c": {
                "d": {
                    "something": "default",
                    "e": {
                        "f": {
                            "g": {
                                "h": {
                                    ... ,

如果对象的确切值和名称已知,您可以对其进行正则表达式替换,例如:

jsonString.replaceAll("\"REMOVEME\" ?: ?\"unwanted\"[,}]","")

否则如果只知道key,还是可以用稍微复杂一点的表达式来匹配:

\"REMOVEME\" ?: ?.+[,}]

或者,如果只知道该值,则向后推。

\".+\" ?: ?\"unwanted\"[,}]

如果它们都是动态的,但您仍然有它们的列表,您应该能够遍历它们,动态地将每个 key/value 更改为匹配的。

我不确定您尝试从节点中删除 属性 的方式,但是可以删除 属性 我是使用 jackson

做到的
String json = "{\n" + 
            "    \"a\": {\n" + 
            "        \"REMOVEME\" : \"unwanted\",\n" + 
            "        \"b\": {\n" + 
            "            \"REMOVEME\" : \"unwanted\"\n" + 
            "        }\n" + 
            "    }\n" + 
            "}";

    JsonNode node = objectMapper.readTree(json);
    ObjectNode node1 = (ObjectNode) node.get("a");

    node1.remove("REMOVEME");
    System.out.println(node1);  // {"b":{"REMOVEME":"unwanted"}}
    System.out.println(node);   // {"a":{"b":{"REMOVEME":"unwanted"}}}

解决这个问题的一种方法是使用 Gson 进行流式令牌解析。此方法可以轻松处理非常大的 JSON 字符串。

前后示例:

{"a":{"removeme":"unwanted","b":{"c":{"removeme":{"x":1},"d":{"e":123}}}}}
{"a":{"b":{"c":{"d":{"e":123}}}}}

基本测试工具:

String rawJson = "{\"a\":{\"removeme\":\"unwanted\",\"b\":{\"c\":{\"removeme\":{\"x\":1},\"d\":{\"e\":123}}}}}";

final Gson gson = new GsonBuilder().create();
JsonReader reader = gson.newJsonReader( new StringReader( rawJson ) );

StringWriter outWriter = new StringWriter();
JsonWriter writer = gson.newJsonWriter( outWriter );

JsonStreamFilter.streamFilter( reader, writer, Arrays.asList( "removeme" ) );

System.out.println( rawJson );
System.out.println( outWriter.toString() );

分词和过滤魔法:

public class JsonStreamFilter
{
    /**
     * Filter out all properties with names included in the `propertiesToRemove` list.
     * 
     * @param reader JsonReader to read in the JSON token
     * @param writer JsonWriter to accept modified JSON tokens
     * @param propertiesToRemove List of property names to remove
     * @throws IOException
     * @see Gson docs at https://sites.google.com/site/gson/streaming
     */
    public static void streamFilter(
        final JsonReader reader,
        final JsonWriter writer,
        final List<String> propertiesToRemove
    ) throws IOException
    {
        while ( true )
        {
            JsonToken token = reader.peek();
            switch ( token )
            {
                case BEGIN_ARRAY:
                    reader.beginArray();
                    writer.beginArray();
                    break;
                case END_ARRAY:
                    reader.endArray();
                    writer.endArray();
                    break;
                case BEGIN_OBJECT:
                    reader.beginObject();
                    writer.beginObject();
                    break;
                case END_OBJECT:
                    reader.endObject();
                    writer.endObject();
                    break;
                case NAME:
                    String name = reader.nextName();

                    // Skip all nested structures stemming from this property
                    if ( propertiesToRemove.contains( name ) )
                    {
                        reader.skipValue();
                        break;
                    }

                    writer.name( name );
                    break;
                case STRING:
                    String s = reader.nextString();
                    writer.value( s );
                    break;
                case NUMBER:
                    String n = reader.nextString();
                    writer.value( new BigDecimal( n ) );
                    break;
                case BOOLEAN:
                    boolean b = reader.nextBoolean();
                    writer.value( b );
                    break;
                case NULL:
                    reader.nextNull();
                    writer.nullValue();
                    break;
                case END_DOCUMENT:
                    return;
            }
        }
    }
}