当前位置: 首页 > 知识库问答 >
问题:

用javax“深度替换”。json。JsonObject公司

林德辉
2023-03-14

替换javax中的值。json。JsonObject不能直接使用,因为javax。json。JsonObject实现了一个不可变的映射。为了做到这一点,您必须创建一个新的JsonObject,并将值从原来的JsonObject复制到新的JsonObject中,同时替换要替换的值。我找到了如何使用“简单”JsonObject实现这一点的示例,其中没有嵌套的JsonObject。我要寻找的是一个通用的replace实现,其中传递一个JsonObject、属性名和新值。这个方法应该“遍历”JsonObject并替换属性(无论它在对象层次结构中的何处),而保持其他属性不变。例如,这是我最初的JsonObject

{
   "Attr1":number1,
   "Attr2":number2,
   "Attr3":number3,
   "Attr4":[
      "string1"
   ],
   "Attr5":[
      {
         "Attr6":[
            {
               "Attr7":"string2",
               "Attr8":"string3",
               "$Attr9":number4
            },
            {
               "Attr7":"string4",
               "Attr8":"string5",
               "Attr9":number5
            }
         ],
         "Attr10":number6,
         "Attr14":{
            "Attr10":"string6",
            "Attr11":"string7",
            "Attr12":"string8"
         },
         "Attr13":[
            "string9",
            "string10"
         ],
         "Attr14":"string11"
      }
   ]
}

我想用一个字符串数组代替Attr6,而不是JsonObject数组:

"Attr6":["newString1","newString2"],

相应的调用可能类似于replaceValue(JsonObject jObj,String attrName,JsonValue newValue)),其中“jObj”是整个Json,“attrName”是“Attr6”,而“newValue”是包含这两个字符串的JsonArray。

有人能给我举一个实现这样一个功能的例子吗,或者帮我解决这个问题吗?

我自己尝试过这个方法,但它并没有真正起作用,因为构建器是在每次递归迭代中重新创建的(或者更可能是因为它完全错了…:)

public static JsonObject replaceValue( final JsonObject jsonObject, final String jsonKey, final JsonValue jsonValue )
{
  JsonObjectBuilder builder = Json.createObjectBuilder();

  if(jsonObject == null)
  {
    return builder.build();
  }

  Iterator<Entry<String, JsonValue>> it = jsonObject.entrySet().iterator();

  while (it.hasNext())
  {
    @SuppressWarnings( "rawtypes" )
    JsonObject.Entry mapEntry = it.next();

    if (mapEntry.getKey() == jsonKey)
    {
      builder.add(jsonKey, jsonValue);
    }
    else if (ValueType.STRING.equals(((JsonValue) mapEntry.getValue()).getValueType()) || ValueType.NUMBER.equals(((JsonValue) mapEntry.getValue()).getValueType()) || ValueType.TRUE.equals(((JsonValue) mapEntry.getValue()).getValueType()) ||
        ValueType.FALSE.equals(((JsonValue) mapEntry.getValue()).getValueType()) || (JsonValue) mapEntry.getValue() == null || "schemas".equalsIgnoreCase((String) mapEntry.getKey()))
    {
      builder.add(mapEntry.getKey().toString(), (JsonValue) mapEntry.getValue());
    }
    else if (ValueType.OBJECT.equals(((JsonValue) mapEntry.getValue()).getValueType()))
    {
      JsonObject modifiedJsonobject = (JsonObject) mapEntry.getValue();
      if (modifiedJsonobject != null)
      {
        replaceValue(modifiedJsonobject, jsonKey, jsonValue);
      }
    }
    else if (ValueType.ARRAY.equals(((JsonValue) mapEntry.getValue()).getValueType()))
    {
      for (int i = 0; i < ((JsonValue) mapEntry.getValue()).asJsonArray().size(); i++)
      {
        replaceValue((JsonObject) ((JsonValue) mapEntry.getValue()).asJsonArray().get(i), jsonKey, jsonValue);
      }
    }
  }

  return builder.build();
}

共有3个答案

蒋俊
2023-03-14

如果您不想使用流式API(正如我在另一个答案中所使用的那样),我认为您可以实现一种更紧凑的方法,与您的方法类似,使用JsonObjectBuilder和JSONOArayBuilder,以及递归:

private static JsonStructure iterate(final JsonStructure json) {
    if (json.getValueType().equals(ValueType.OBJECT)) {
        JsonObjectBuilder builder = Json.createObjectBuilder();
        json.asJsonObject().forEach((key, value) -> {
            switch (value.getValueType()) {
                case OBJECT:
                    if (key.equals(targetKey)) {
                        builder.add(key, replacementJson);
                    } else {
                        builder.add(key, iterate(value.asJsonObject()));
                    }   break;
                case ARRAY:
                    if (key.equals(targetKey)) {
                        builder.add(key, replacementJson);
                    } else {
                        builder.add(key, iterate(value.asJsonArray()));
                    }   break;
                default:
                    if (key.equals(targetKey)) {
                        builder.add(key, replacementJson);
                    } else {
                        builder.add(key, value);
                    }   break;
            }
        });
        return builder.build();
    } else if (json.getValueType().equals(ValueType.ARRAY)) {
        JsonArrayBuilder builder = Json.createArrayBuilder();
        json.asJsonArray().forEach((value) -> {
            switch (value.getValueType()) {
                case OBJECT:
                    builder.add(iterate(value.asJsonObject()));
                    break;
                case ARRAY:
                    builder.add(iterate(value.asJsonArray()));
                    break;
                default:
                    builder.add(value);
                    break;
            }
        });
        return builder.build();
    }
    return null;
}

就我个人而言,阅读这个递归代码比阅读我另一个答案中的流代码更难。但它肯定更简洁。

它的工作原理是向下迭代每个JSON对象和数组的嵌套级别,然后从最深的嵌套级别向外构建原始数据的副本。当它找到指定的替换键时,它使用相关的替换JSON作为键的值。

可以按如下方式调用上述方法-它可以很好地打印最终结果:

java prettyprint-override">final JsonStructure jsonOriginal = Json.createReader(new StringReader(JSONSTRING)).readObject();

final JsonStructure jsonCopy = iterate(jsonOriginal);

Map<String, Boolean> config = new HashMap<>();
config.put(JsonGenerator.PRETTY_PRINTING, true);
String jsonString;
JsonWriterFactory writerFactory = Json.createWriterFactory(config);
try ( Writer writer = new StringWriter()) {
    writerFactory.createWriter(writer).write(jsonCopy);
    jsonString = writer.toString();
}
System.out.println(jsonString);

对于我的替代JSON,我使用了这个,显示了一些测试数据示例:

private final static String targetKey = "Attr6";

//private final static JsonStructure replacementJson = Json.createArrayBuilder()
//        .add("newString1")
//        .add("newString2").build();

private final static JsonStructure replacementJson = Json.createObjectBuilder()
        .add("newkey1", "newString1")
        .add("newkey2", "newString2").build();

因此,使用与我的另一个答案相同的起始JSON,此代码生成以下内容:

{
    "Attr0": null,
    "Attr1": true,
    "Attr2": false,
    "Attr3": 3,
    "Attr4": [
        "string1"
    ],
    "Attr5": [
        {
            "Attr6": {
                "newkey1": "newString1",
                "newkey2": "newString2"
            },
            "Attr10": 6,
            "Attr14": {
                "Attr10": "string6",
                "Attr11": "string7",
                "Attr12": "string8"
            },
            "Attr13": [
                "string9",
                123.45,
                false
            ],
            "Attr15": "string11"
        }
    ]
}
沈博延
2023-03-14

从Kolban在本文中的回答中获得线索后,将JSON字符串转换为HashMap,我应该找到一个解决方案:

public class JsonUtils
{

  public static JsonObject replaceValue( final JsonObject jsonObject, final String jsonKey, final Object jsonValue )
  {
    JsonObjectBuilder builder = Json.createObjectBuilder();

    if (jsonObject != JsonObject.NULL)
    {
      builder = replace(jsonObject, jsonKey, jsonValue, builder);
    }

    return builder.build();
  }

  private static JsonObjectBuilder replace( final JsonObject jsonObject, final String jsonKey, final Object jsonValue, final JsonObjectBuilder builder )
  {
    Iterator<Entry<String, JsonValue>> it = jsonObject.entrySet().iterator();

    while (it.hasNext())
    {
      @SuppressWarnings( "rawtypes" )
      JsonObject.Entry mapEntry = it.next();

      String key = mapEntry.getKey().toString();
      Object value = mapEntry.getValue();

      if (key.equalsIgnoreCase(jsonKey))
      {
        if (jsonValue instanceof String)
        {
          builder.add(jsonKey, (String) jsonValue);
        }
        else
        {
          builder.add(jsonKey, (JsonValue) jsonValue);
        }
        
        // here you can add the missing casting you need

        continue;
      }

      if (value instanceof JsonArray)
      {
        value = toJsonArray((JsonArray) value, jsonKey, jsonValue, builder);
      }
      else if (value instanceof JsonObject)
      {
        JsonObjectBuilder newBuilder = Json.createObjectBuilder();

        value = replace((JsonObject) value, jsonKey, jsonValue, newBuilder);

        if (value instanceof JsonObjectBuilder)
        {
          value = ((JsonObjectBuilder) value).build();
        }
      }

      builder.add(key, (JsonValue) value);
    }

    return builder;
  }

  private static JsonArray toJsonArray( final JsonArray array, final String jsonKey, final Object jsonValue, final JsonObjectBuilder builder )
  {
    JsonArrayBuilder jArray = Json.createArrayBuilder();

    for (int i = 0; i < array.size(); i++)
    {
      Object value = array.get(i);

      if (value instanceof JsonArray)
      {
        value = toJsonArray((JsonArray) value, jsonKey, jsonValue, builder);
      }
      else if (value instanceof JsonObject)
      {
        JsonObjectBuilder newBuilder = Json.createObjectBuilder();

        value = replace((JsonObject) value, jsonKey, jsonValue, newBuilder);

        if (value instanceof JsonObjectBuilder)
        {
          value = ((JsonObjectBuilder) value).build();
        }
      }

      jArray.add((JsonValue) value);
    }

    return jArray.build();
  }

请记住,如果要替换的键在整个JsonObject中是唯一的,那么这是可行的。

任何改进都是值得赞赏的。。。

卫嘉谊
2023-03-14

这是解决问题的另一种方法,它使用由javax.json.stream.JsonParser提供的流解析器。这会从JSON源代码生成一个令牌流,使用javax.json.stream.JsonParser。事件值来描述令牌的类型(例如START_OBJECTKEY_NAME等等)。

对我们来说最重要的是,解析器上有skipObject()skipArray()方法,它们允许我们删除源JSON中不需要的部分。

总体方法是构建一个新版本的JSON,逐个令牌,作为一个字符串,当我们到达JSON中的相关位置(或多个位置)时替换部分。

最后,我们将新字符串转换回一个对象,以便可以很好地打印它。

这种方法中没有使用递归。

import java.io.IOException;
import javax.json.Json;
import javax.json.stream.JsonParser;
import javax.json.stream.JsonParser.Event;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import javax.json.JsonObject;
import javax.json.JsonWriterFactory;
import javax.json.stream.JsonGenerator;

public class StreamDemo {

    public static void doStream() throws IOException {
        JsonParser jsonParser = Json.createParser(new StringReader(JSONSTRING));

        StringBuilder sb = new StringBuilder();
        Event previous = null;

        String targetKeyName = "Attr6";
        String replacement = "[\"newString1\",\"newString2\"]";
        // This event reflects the end of the "replacement" string - namely "]".
        // We need this because this event may be different from the replaced event.
        Event replacementPreviousEvent = Event.END_ARRAY;

        // Used when we find the target key for replacement:
        boolean doReplacement = false;

        while (jsonParser.hasNext()) {
            Event event = jsonParser.next();
            if (doReplacement) {
                // Skip over the structure we want to replace:
                if (event.equals(Event.START_OBJECT)) {
                    jsonParser.skipObject();
                } else if (event.equals(Event.START_ARRAY)) {
                    jsonParser.skipArray();
                }
                // Write the replacement fragment here:
                sb.append(replacement);
                // Move to the next event in the stream:
                event = jsonParser.next();
                previous = replacementPreviousEvent;
                doReplacement = false;
            }

            if (Event.KEY_NAME.equals(event)
                    && jsonParser.getString().equals(targetKeyName)) {
                doReplacement = true;
            }

            switch (event) {
                case START_OBJECT:
                    if (Event.END_OBJECT.equals(previous)) {
                        sb.append(",");
                    }
                    sb.append("{");
                    break;
                case END_OBJECT:
                    sb.append("}");
                    break;
                case START_ARRAY:
                    sb.append("[");
                    break;
                case END_ARRAY:
                    sb.append("]");
                    break;
                case KEY_NAME:
                    sb = previousWasAValue(previous, sb);
                    sb = previousWasAnEnd(previous, sb);
                    sb.append("\"").append(jsonParser.getString()).append("\":");
                    break;
                case VALUE_STRING:
                    sb = previousWasAValue(previous, sb);
                    sb.append("\"").append(jsonParser.getString()).append("\"");
                    break;
                case VALUE_NUMBER:
                    sb = previousWasAValue(previous, sb);
                    if (jsonParser.isIntegralNumber()) {
                        sb.append(jsonParser.getLong());
                    } else {
                        sb.append(jsonParser.getBigDecimal().toPlainString());
                    }
                    break;
                case VALUE_TRUE:
                    sb = previousWasAValue(previous, sb);
                    sb.append("true");
                    break;
                case VALUE_FALSE:
                    sb = previousWasAValue(previous, sb);
                    sb.append("false");
                    break;
                case VALUE_NULL:
                    sb = previousWasAValue(previous, sb);
                    sb.append("null");
                    break;
                default:
                    break;
            }
            previous = event;
        }
        
        // At the end, pretty-print the new JSON:
        JsonObject modifiedObject = Json.createReader(new StringReader(sb.toString())).readObject();
        Map<String, Boolean> config = new HashMap<>();
        config.put(JsonGenerator.PRETTY_PRINTING, true);
        String jsonString;
        JsonWriterFactory writerFactory = Json.createWriterFactory(config);
        try ( Writer writer = new StringWriter()) {
            writerFactory.createWriter(writer).write(modifiedObject);
            jsonString = writer.toString();
        }
        System.out.println(jsonString);
    }

    private static StringBuilder previousWasAValue(Event previous, StringBuilder sb) {
        // The current value follows another value - so a separating comma is needed:
        if (Event.VALUE_STRING.equals(previous)
                || Event.VALUE_NUMBER.equals(previous)
                || Event.VALUE_TRUE.equals(previous)
                || Event.VALUE_FALSE.equals(previous)
                || Event.VALUE_NULL.equals(previous)) {
            sb.append(",");
        }
        return sb;
    }

    private static StringBuilder previousWasAnEnd(Event previous, StringBuilder sb) {
        // The current key follows the end of an object or an array, so a 
        // separating comma is needed:
        if (Event.END_OBJECT.equals(previous)
                || Event.END_ARRAY.equals(previous)) {
            sb.append(",");
        }
        return sb;
    }

    private static final String JSONSTRING
            = """
    {
       "Attr0": null,
       "Attr1": true,
       "Attr2": false,
       "Attr3": 3,
       "Attr4": [
               "string1"
       ],
       "Attr5": [{
               "Attr6": [{
                               "Attr7": "string2",
                               "Attr8": "string3",
                               "Attr9": 4
                       },
                       {
                               "Attr7": "string4",
                               "Attr8": "string5",
                               "Attr9": 5
                       }
               ],
               "Attr10": 6,
               "Attr14": {
                       "Attr10": "string6",
                       "Attr11": "string7",
                       "Attr12": "string8"
               },
               "Attr13": [
                       "string9",
                       123.45,
                       false
               ],
               "Attr15": "string11"
       }]
    }
    """;

}
 类似资料:
  • 主要内容:1 简单JSON与JSONObject的转换,2 JSON数组与JSONArray的转换,3 复杂JSON与JSONObject的转换1 简单JSON与JSONObject的转换 1.1 简单JSON转为JSONObject MainApp: 运行效果为: 1.2 JSONObject转为简单JSON MainApp: 运行效果为: 2 JSON数组与JSONArray的转换 2.1 JSON数组转为JSONArray MainApp: 运行效果为: 2.2 JSONArray转为JS

  • 在开始新的Jakarta(9)项目时,我需要使用“javax”JSON-P API处理旧的(遗留库和模块)。通常我们以这个错误结束: java:不兼容类型:jakarta。json。JsonObject无法转换为javax。json。JsonObject公司 不幸的是,旧代码使用了各种javax。json。*类,尤其是JsonObject,这些类在新模块的方法中不可用(因为它们使用的是jakart

  • 我们有这样一个json字符串: 我们想使用下面的代码来替换后一个key6的值: 但是由于key6在jsonstring中显示了两次,我们替换了错误的值。上面的json结构只是一个例子,“key6”的json路径在不同的json中是完全不同的。我们无法为这种替换编写确切的jsonpath。我们怎么能只是用java替换没有孩子的“key6”的值?多谢了。

  • 问题内容: 我正在使用Eclipse IDE,并且正在编写servlet。Servlet应该接受html文件中的值,并相应地返回JSON响应。 我的doPost()是: 当我在Eclipse中运行此servlet时,会出现一个文件下载对话框。 当使用Tomcat在Eclipse外部运行时,出现错误: 行Server1.doPost(Server1.java:25)指的是 我已经将org.json.

  • 介绍 JSONObject代表一个JSON中的键值对象,这个对象以大括号包围,每个键值对使用,隔开,键与值使用:隔开,一个JSONObject类似于这样: { "key1":"value1", "key2":"value2" } 此处键部分可以省略双引号,值为字符串时不能省略,为数字或波尔值时不加双引号。 使用 创建 JSONObject json1 = JSONUtil.createO