簡體   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