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

时间:2014-08-20 14:30:48

标签: java json

Inshort:我试图找到一些api,可以通过将第一个参数作为jsonString,第二个参数作为JSONPath来改变值,第三个参数将是该参数的新值。但是,我发现的只是这个...... https://code.google.com/p/json-path/

这个api允许我在JSON String中找到任何值。但是,我找不到简单的方法来更新任何键的值。例如,这是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
    }
   }
 }

我可以通过这样做来获取自行车的颜色。

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

但我正在寻找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. 

我将这些选项排除在外,

  • 创建一个新的JSON字符串。
  • 创建一个JSON对象来处理更改的值并将其转换回jsonstring

原因:我对不同类型的服务有大约500个不同的请求,这些请求返回不同的json结构。所以,我不想总是手动创建新的JSON字符串。因为,ID在json结构中是动态的。

非常感谢任何想法或方向。

使用以下答案更新此问题。

  1. 复制MutableJson.java
  2. 复制此小片段并根据需要进行修改。

    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());
    
    }
    
  3. 使用这些库。

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

4 个答案:

答案 0 :(得分:9)

问题是你想要的功能已经是JsonPath的一个未记录的功能。使用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("$"));

答案 1 :(得分:5)

假设已解析的JSON可以在内存中表示为Map,您可以构建类似于JsonPath的API,如下所示:

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

我已经快速完成了对可以遍历json树的简单特定路径(不支持条件和通配符)的脏实现的要点,例如。 $ .store.name,$ .store.books [0] .isbn。这是:MutableJson.java。它肯定需要改进,但可以提供一个良好的开端。

用法示例:

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);
    }
}

使用Jackson可以很容易地将JSON字符串解析为Map:

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

答案 2 :(得分:1)

回答将来登陆此页面的人员,以供参考。

您可以考虑使用jsonpatch的Java实现。可以找到RFC here

  

JSON Patch是一种用于描述JSON文档更改的格式。它可用于避免仅在部件发生更改时发送整个文档。当与HTTP PATCH方法结合使用时,它允许以符合标准的方式对HTTP API进行部分更新。

您可以指定需要执行的操作(替换,添加....),必须执行的json路径以及应该使用的值。

再次,从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" }
   ]

对于Java实现,我自己没有使用它,但你可以尝试https://github.com/fge/json-patch

答案 3 :(得分:0)

因此,为了更改JSon字符串中的值,有两个步骤:

  1. 解析JSon
  2. 修改相应的字段
  3. 您正在尝试优化第2步,但要了解您无法避免第1步。查看Json路径源代码(实际上,它只是Jackson的包装器) ,请注意,在能够吐出读取值之前,它确实对Json字符串进行了完整的解析。每次拨打read()时都会进行解析,例如它没有被缓存。

    我认为这项任务非常具体,你必须自己编写。这就是我要做的事情:

    1. 创建一个对象,该对象表示已解析的Json字符串中的数据。
      • 确保此对象作为其字段的一部分包含您不希望经常更改的Json String部分。
    2. 在您选择的Json框架中创建一个自定义反序列化器,以正确填充字段。
    3. 创建一个使用缓存的String件的自定义序列化程序,以及您希望更改的数据
    4. 我认为问题的确切范围非常不寻常,因此库不太可能存在。当程序收到Json String时,大多数时候它想要的是完全反序列化的对象 - 它需要将此对象转发到其他地方是不常见的。