Подтвердить что ты не робот

Поиск Json-path/(любого API) для обновления любого значения в заданной json-строке в Java

Включение: я пытаюсь найти 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. Поскольку идентификаторы являются динамическими в структуре json.

Любая идея или направление очень ценится.

Обновление этого вопроса с помощью следующего ответа.

  • Скопировать MutableJson.java.
  • Скопируйте этот маленький фрагмент и измените, как вам нужно.

    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;
4b9b3361

Ответ 1

Предполагая, что разобранный JSON может быть представлен в памяти как Карта, вы можете создать API, похожий на JsonPath, который выглядит следующим образом:

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

Я быстро сделал суть грязной реализации для простых конкретных путей (без поддержки условий и подстановочных знаков), которые могут проходить через json tree, например. $.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 может быть легко проанализирована на карте с использованием Джексона:

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

Ответ 2

Дело в том, что функциональность, которую вы хотите, уже является недокументированной функцией 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("$"));

Ответ 3

Просто ответьте, чтобы люди приземлились на этой странице в будущем для справки.

Вы можете использовать реализацию Java jsonpatch. RFC можно найти здесь

JSON Patch - это формат для описания изменений в документе JSON. Его можно использовать, чтобы избежать отправки целого документа, когда только часть изменилась. При использовании в сочетании с методом HTTP PATCH он позволяет частично обновлять API-интерфейсы HTTP стандартным образом.

Вы можете указать операцию, которая должна быть выполнена (заменить, добавить....), путь 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

Ответ 4

Итак, чтобы изменить значение в строке JSon, есть два шага:

  • Разберите JSon
  • Измените соответствующее поле

Вы пытаетесь оптимизировать шаг 2, но поймите, что вы не сможете избежать шага 1. Глядя на исходный код Json-path (который, на самом деле, является всего лишь оберткой вокруг Jackson), обратите внимание, что он выполняет полный анализ строки Json, прежде чем сможет выплеснуть значение чтения. Он выполняет этот анализ каждый раз, когда вы вызываете read(), например. он не кэшируется.

Я думаю, что эта задача достаточно конкретна, что вам придется писать ее самостоятельно. Вот что я буду делать:

  • Создайте объект, представляющий данные в синтаксической цепочке Json.
    • Убедитесь, что этот объект имеет как часть его полей Json String, которые вы не ожидаете часто изменять.
  • Создайте собственный десериализатор в рамках Json по вашему выбору, который правильно заполнит поля.
  • Создайте собственный Сериализатор, который использует кешированные фрагменты String, а также данные, которые вы ожидаете изменить.

Я думаю, что точная область вашей проблемы достаточно необычна, что для нее вряд ли существует библиотека. Когда программа получает Json String, большую часть времени он хочет полностью десериализованный объект - необычно, что ему нужно ПЕРЕДАТЬ этот объект в другом месте.