简体   繁体   English

在Java中寻找Json-path /(任何API)更新给定json字符串中的任何值

[英]Looking for Json-path/(any API) to update any value in given json string in Java

Inshort : I am trying to find some api that could just change the value by taking first parameter as jsonString , second parameter as JSONPath and third will be new value of that parameter. 简而言之:我正在尝试找到一些可以通过将第一个参数作为jsonString,将第二个参数作为JSONPath并将第三个参数作为该参数的新值来更改值的api。 But, all I found is this.. https://code.google.com/p/json-path/ 但是,我发现的只是这个。.https ://code.google.com/p/json-path/

This api allows me to find any value in JSON String. 这个API允许我在JSON字符串中查找任何值。 But, I am not finding easy way to update the value of any key. 但是,我没有找到更新任何键值的简便方法。 For example, Here is a book.json. 例如,这是一个book.json。

{
"store":{
    "book":[
        {
            "category":"reference",
            "author":"Nigel Rees",
            "title":"Sayings of the Century",
            "price":8.95
        },
        {
            "category":"fiction",
            "author":"Evelyn Waugh",
            "title":"Sword of Honour",
            "price":12.99,
            "isbn":"0-553-21311-3"
        }
    ],
    "bicycle":{
        "color":"red",
        "price":19.95
    }
   }
 }

I can access color of bicycle by doing this. 这样可以进入自行车的颜色。

String bicycleColor = JsonPath.read(json, "$.store.bicycle.color");

But I am looking for a method in JsonPath or other api some thing like this 但是我正在JsonPath或其他api中寻找这样的方法

    JsonPath.changeNodeValue(json, "$.store.bicycle.color", "green");
    String bicycleColor = JsonPath.read(json, "$.store.bicycle.color");
    System.out.println(bicycleColor);  // This should print "green" now. 

I am excluding these options, 我不包括这些选项,

  • Create a new JSON String. 创建一个新的JSON String。
  • Create a JSON Object to deal with changing value and convert it back to jsonstring 创建一个JSON对象以处理更改的值并将其转换回jsonstring

Reason: I have about 500 different requests for different types of service which return different json structure. 原因:对于不同类型的服务,我有大约500个不同的请求,它们返回不同的json结构。 So, I do not want to manually create new JSON string always. 因此,我不想总是手动创建新的JSON字符串。 Because, IDs are dynamic in json structure. 因为,ID在json结构中是动态的。

Any idea or direction is much appreciated. 任何想法或方向将不胜感激。

Updating this question with following answer. 使用以下答案更新此问题。

  1. Copy MutableJson.java . 复制MutableJson.java
  2. copy this little snippet and modify as per you need. 复制此小片段,然后根据需要进行修改。

     private static void updateJsonValue() { JSONParser parser = new JSONParser(); JSONObject jsonObject = new JSONObject(); FileReader reader = null; try { File jsonFile = new File("path to book.json"); reader = new FileReader(jsonFile); jsonObject = (JSONObject) parser.parse(reader); } catch (Exception ex) { System.out.println(ex.getLocalizedMessage()); } Map<String, Object> userData = null; try { userData = new ObjectMapper().readValue(jsonObject.toJSONString(), Map.class); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } MutableJson json = new MutableJson(userData); System.out.println("Before:\\t" + json.map()); json.update("$.store.book[0].author", "jigish"); json.update("$.store.book[1].category", "action"); System.out.println("After:\\t" + json.map().toString()); } 

Use these libraries. 使用这些库。

  • import org.json.simple.JSONObject; 导入org.json.simple.JSONObject;
  • import org.json.simple.parser.JSONParser; 导入org.json.simple.parser.JSONParser;
  • import org.codehaus.jackson.map.ObjectMapper; 导入org.codehaus.jackson.map.ObjectMapper;

The thing is that the functionality you want is already an undocumented feature of JsonPath. 问题是所需的功能已经是JsonPath的未记录功能。 Example using your json structure: 使用json结构的示例:

String json = "{ \"store\":{ \"book\":[ { \"category\":\"reference\", \"author\":\"Nigel Rees\", \"title\":\"Sayings of the Century\", \"price\":8.95 }, { \"category\":\"fiction\", \"author\":\"Evelyn Waugh\", \"title\":\"Sword of Honour\", \"price\":12.99, \"isbn\":\"0-553-21311-3\" } ], \"bicycle\":{ \"color\":\"red\", \"price\":19.95 } } }";
DocumentContext doc = JsonPath.parse(json).
    set("$.store.bicycle.color", "green").
    set("$.store.book[0].price", 9.5);
String newJson = new Gson().toJson(doc.read("$"));

Assuming that parsed JSON can be represented in memory as a Map, you can build an API similar to JsonPath that looks like: 假设已解析的JSON可以在内存中表示为Map,则可以构建类似于JsonPath的API,如下所示:

void update(Map<String, Object> json, String path, Object newValue);

I've quickly done a gist of a dirty implementation for simple specific paths (no support for conditions and wildcards) that can traverse json tree, Eg $.store.name, $.store.books[0].isbn. 我很快就针对可以遍历json树的简单特定路径(不支持条件和通配符)做了一个肮脏的实现要点,例如$ .store.name,$。store.books [0] .isbn。 Here it is: MutableJson.java . 它是: MutableJson.java It definitely needs improvement, but can give a good start. 它肯定需要改进,但是可以提供良好的开端。

Usage example: 用法示例:

import java.util.*;

public class MutableJson {

    public static void main(String[] args) {
        MutableJson json = new MutableJson(
                new HashMap<String, Object>() {{
                    put("store", new HashMap<String, Object>() {{
                        put("name", "Some Store");
                        put("books", Arrays.asList(
                                new HashMap<String, Object>() {{
                                    put("isbn", "111");
                                }},
                                new HashMap<String, Object>() {{
                                    put("isbn", "222");
                                }}
                        ));
                    }});
                }}
        );

        System.out.println("Before:\t" + json.map());

        json.update("$.store.name", "Book Store");
        json.update("$.store.books[0].isbn", "444");
        json.update("$.store.books[1].isbn", "555");

        System.out.println("After:\t" + json.map());
    }

    private final Map<String, Object> json;

    public MutableJson(Map<String, Object> json) {
        this.json = json;
    }

    public Map<String, Object> map() {
        return json;
    }

    public void update(String path, Object newValue) {
        updateJson(this.json, Path.parse(path), newValue);
    }

    private void updateJson(Map<String, Object> data, Iterator<Token> path, Object newValue) {
        Token token = path.next();
        for (Map.Entry<String, Object> entry : data.entrySet()) {
            if (!token.accept(entry.getKey(), entry.getValue())) {
                continue;
            }

            if (path.hasNext()) {
                Object value = token.value(entry.getValue());
                if (value instanceof Map) {
                    updateJson((Map<String, Object>) value, path, newValue);
                }
            } else {
                token.update(entry, newValue);
            }
        }
    }
}

class Path {
    public static Iterator<Token> parse(String path) {
        if (path.isEmpty()) {
            return Collections.<Token>emptyList().iterator();
        }
        if (path.startsWith("$.")) {
            path = path.substring(2);
        }

        List<Token> tokens = new ArrayList<>();
        for (String part : path.split("\\.")) {
            if (part.matches("\\w+\\[\\d+\\]")) {
                String fieldName = part.substring(0, part.indexOf('['));
                int index = Integer.parseInt(part.substring(part.indexOf('[')+1, part.indexOf(']')));
                tokens.add(new ArrayToken(fieldName, index));
            } else {
                tokens.add(new FieldToken(part));
            }
        };

        return tokens.iterator();
    }
}

abstract class Token {

    protected final String fieldName;

    Token(String fieldName) {
        this.fieldName = fieldName;
    }

    public abstract Object value(Object value);

    public abstract boolean accept(String key, Object value);

    public abstract void update(Map.Entry<String, Object> entry, Object newValue);
}

class FieldToken extends Token {

    FieldToken(String fieldName) {
        super(fieldName);
    }

    @Override
    public Object value(Object value) {
        return value;
    }

    @Override
    public boolean accept(String key, Object value) {
        return fieldName.equals(key);
    }

    @Override
    public void update(Map.Entry<String, Object> entry, Object newValue) {
        entry.setValue(newValue);
    }
}

class ArrayToken extends Token {

    private final int index;

    ArrayToken(String fieldName, int index) {
        super(fieldName);
        this.index = index;
    }

    @Override
    public Object value(Object value) {
        return ((List) value).get(index);
    }

    @Override
    public boolean accept(String key, Object value) {
        return fieldName.equals(key) && value instanceof List && ((List) value).size() > index;
    }

    @Override
    public void update(Map.Entry<String, Object> entry, Object newValue) {
        List list = (List) entry.getValue();
        list.set(index, newValue);
    }
}

A JSON string can be easily parsed into a Map using Jackson: 使用Jackson可以轻松地将JSON字符串解析为Map:

Map<String,Object> userData = new ObjectMapper().readValue("{ \"store\": ... }", Map.class);

Just answering for folks landing on this page in future for reference. 只是回答将来登陆此页面的人员以供参考。

You could consider using a Java implementation of jsonpatch . 您可以考虑使用jsonpatch的Java实现。 RFC can be found here RFC可以在这里找到

JSON Patch is a format for describing changes to a JSON document. JSON修补程序是一种描述JSON文档更改的格式。 It can be used to avoid sending a whole document when only a part has changed. 当只更改了一部分时,可用于避免发送整个文档。 When used in combination with the HTTP PATCH method it allows partial updates for HTTP APIs in a standards compliant way. 与HTTP PATCH方法结合使用时,它允许以符合标准的方式对HTTP API进行部分更新。

You can specify the operation that needs to be performed (replace, add....), json path at which it has to be performed, and the value which should be used. 您可以指定需要执行的操作(替换,添加...),必须执行的json路径以及应使用的值。

Again, taking example from the RFC : 同样,以RFC为例:

 [
     { "op": "test", "path": "/a/b/c", "value": "foo" },
     { "op": "remove", "path": "/a/b/c" },
     { "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] },
     { "op": "replace", "path": "/a/b/c", "value": 42 },
     { "op": "move", "from": "/a/b/c", "path": "/a/b/d" },
     { "op": "copy", "from": "/a/b/d", "path": "/a/b/e" }
   ]

For Java implementation, I have not used it myself, but you can give a try to https://github.com/fge/json-patch 对于Java实现,我自己没有使用过,但是您可以尝试https://github.com/fge/json-patch

So in order to change a value within a JSon string, there are two steps: 因此,为了更改JSon字符串中的值,有两个步骤:

  1. Parse the JSon 解析JSon
  2. Modify the appropriate field 修改适当的字段

You are trying to optimize step 2, but understand that you are not going to be able to avoid step 1. Looking at the Json-path source code (which, really, is just a wrapper around Jackson ), note that it does do a full parse of the Json string before being able to spit out the read value. 您正在尝试优化第2步,但了解到您将无法避免第1步。查看Json-path源代码(实际上,它只是Jackson的包装),请注意它确实做了在能够吐出读取值之前对Json字符串进行完整解析。 It does this parse every time you call read() , eg it is not cached. 每次您调用read() ,它都会进行此解析,例如,它不会被缓存。

I think this task is specific enough that you're going to have to write it yourself. 我认为这个任务足够具体,您将不得不自己编写它。 Here is what I would do: 这是我会做的:

  1. Create an object that represents the data in the parsed Json string. 创建一个表示已解析的Json字符串中的数据的对象。
    • Make sure this object has, as part of it's fields, the Json String pieces that you do not expect to change often. 确保此对象作为字段的一部分具有您不希望经常更改的Json String片段。
  2. Create a custom Deserializer in the Json framework of your choice that will populate the fields correctly. 在您选择的Json框架中创建一个自定义反序列化器,以正确填充字段。
  3. Create a custom Serializer that uses the cached String pieces, plus the data that you expect to change 创建一个使用缓存的String片段以及您希望更改的数据的自定义序列化程序

I think the exact scope of your problem is unusual enough that it is unlikely a library already exists for this. 我认为您所遇到问题的确切范围非常不寻常,以至于不太可能为此存在一个库。 When a program receives a Json String , most of the time what it wants is the fully deserialized object - it is unusual that it needs to FORWARD this object on to somewhere else. 当程序接收到Json String ,大多数情况下它想要的是完全反序列化的对象-很少需要将这个对象转发到其他地方。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM