繁体   English   中英

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

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

简而言之:我正在尝试找到一些可以通过将第一个参数作为jsonString,将第二个参数作为JSONPath并将第三个参数作为该参数的新值来更改值的api。 但是,我发现的只是这个。.https ://code.google.com/p/json-path/

这个API允许我在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
    }
   }
 }

这样可以进入自行车的颜色。

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 String。
  • 创建一个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()); } 

使用这些库。

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

问题是所需的功能已经是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("$"));

假设已解析的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);

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

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

JSON修补程序是一种描述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

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

  1. 解析JSon
  2. 修改适当的字段

您正在尝试优化第2步,但了解到您将无法避免第1步。查看Json-path源代码(实际上,它只是Jackson的包装),请注意它确实做了在能够吐出读取值之前对Json字符串进行完整解析。 每次您调用read() ,它都会进行此解析,例如,它不会被缓存。

我认为这个任务足够具体,您将不得不自己编写它。 这是我会做的:

  1. 创建一个表示已解析的Json字符串中的数据的对象。
    • 确保此对象作为字段的一部分具有您不希望经常更改的Json String片段。
  2. 在您选择的Json框架中创建一个自定义反序列化器,以正确填充字段。
  3. 创建一个使用缓存的String片段以及您希望更改的数据的自定义序列化程序

我认为您所遇到问题的确切范围非常不寻常,以至于不太可能为此存在一个库。 当程序接收到Json String ,大多数情况下它想要的是完全反序列化的对象-很少需要将这个对象转发到其他地方。

暂无
暂无

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

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