How to modify the value of a JsonNode recursively using Jackson

后端 未结 2 1504
南旧
南旧 2020-12-11 11:36

Requirements:
I want to apply some functions on the inner values of the JsonNode. The functions can be different eg:- lowercasing some values o

2条回答
  •  春和景丽
    2020-12-11 12:12

    JsonPath

    You could use JsonPath library which has a better JSON Path handling. When Jackson supports only JSON Pointer draft-ietf-appsawg-json-pointer-03. Take a look on JsonPointer documentation. With JsonPath library you could do that in this way:

    import com.jayway.jsonpath.DocumentContext;
    import com.jayway.jsonpath.JsonPath;
    import net.minidev.json.JSONArray;
    
    import java.io.File;
    import java.io.IOException;
    import java.math.BigDecimal;
    import java.math.RoundingMode;
    import java.util.Arrays;
    import java.util.List;
    import java.util.Map;
    import java.util.function.Function;
    
    public class JsonPathApp {
    
        public static void main(String[] args) throws Exception {
            File jsonFile = new File("./resource/test.json").getAbsoluteFile();
    
            JsonModifier jsonModifier = new JsonModifier(jsonFile);
            Function, Void> lowerCaseName = map -> {
                final String key = "name";
                map.put(key, map.get(key).toString().toLowerCase());
                return null;
            };
            Function, Void> changeDistToNumber = map -> {
                final String key = "dist";
                map.put(key, Integer.parseInt(map.get(key).toString()));
                return null;
            };
            jsonModifier.update("$.values[*].addresses[*]", Arrays.asList(lowerCaseName, changeDistToNumber));
            jsonModifier.print();
        }
    }
    
    class JsonModifier {
    
        private final DocumentContext document;
    
        public JsonModifier(File json) throws IOException {
            this.document = JsonPath.parse(json);
        }
    
        public void update(String path, List, Void>> transformers) {
            JSONArray array = document.read(path);
            for (int i = 0; i < array.size(); i++) {
                Object o = array.get(i);
                transformers.forEach(t -> {
                    t.apply((Map) o);
                });
            }
        }
    
        public void print() {
            System.out.println(document.jsonString());
        }
    }
    

    Your path, should work on JSON object-s which are represented by Map. You can replace keys in given object, add them, remove them just like replacing, adding and removing keys in Map.

    Jackson

    You can of course mock JsonPath feature by iterating over Json Pointer. For each * we need to create loop and iterate over it using counter and until node is not missing. Below you can see simple implementation:

    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.SerializationFeature;
    import com.fasterxml.jackson.databind.node.ObjectNode;
    
    import java.io.File;
    import java.util.Arrays;
    import java.util.List;
    import java.util.function.Function;
    
    public class JsonApp {
    
        public static void main(String[] args) throws Exception {
            File jsonFile = new File("./resource/test.json").getAbsoluteFile();
    
            ObjectMapper mapper = new ObjectMapper();
            mapper.enable(SerializationFeature.INDENT_OUTPUT);
            JsonNode root = mapper.readTree(jsonFile);
    
            Function lowerCaseName = node -> {
                final String key = "name";
                node.put(key, node.get(key).asText().toLowerCase());
                return null;
            };
            Function changeDistToNumber = node -> {
                final String key = "dist";
                node.put(key, Integer.parseInt(node.get(key).asText()));
                return null;
            };
    
            JsonModifier jsonModifier = new JsonModifier(root);
            jsonModifier.updateAddresses(Arrays.asList(lowerCaseName, changeDistToNumber));
    
            System.out.println(mapper.writeValueAsString(root));
        }
    }
    
    class JsonModifier {
    
        private final JsonNode root;
    
        public JsonModifier(JsonNode root) {
            this.root = root;
        }
    
        public void updateAddresses(List> transformers) {
            String path = "/values/%d/addresses/%d";
            for (int v = 0; v < 100; v++) {
                int a = 0;
                do {
                    JsonNode address = root.at(String.format(path, v, a++));
                    if (address.isMissingNode()) {
                        break;
                    }
                    if (address.isObject()) {
                        transformers.forEach(t -> t.apply((ObjectNode) address));
                    }
                } while (true);
                if (a == 0) {
                    break;
                }
            }
        }
    }
    

    This solution is slower than with JsonPath because we need to travers whole JSON tree n times where n number of matching nodes. Of course, our implementation could be a much faster using Streaming API.

提交回复
热议问题