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

2022-09-04 08:15:23

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

这个api允许我在JSON字符串中找到任何值。但是,我没有找到更新任何键的值的简单方法。例如,这里是一本书.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 对象来处理不断变化的值,并将其转换回 json 字符串

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

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

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

  1. 复制可变Json.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());
    
    }
    

使用这些库。

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

答案 1

问题是,你想要的功能已经是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("$"));

答案 2

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

JSON 字符串可以使用 Jackson 轻松解析为 Map:

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