简体   繁体   中英

Java - Access Nested JSONArray values dynamically

I have a JSON file ( myjsonfile.json ) which stores the folder structure along with file contents as a JSON. It is as follows:

{
  "name":"folder1",
  "type":"directory",

  "children":[
    {"name":"info",
      "type":"directory",
      "children": [
        {
        "name":"info1.txt",
        "type":"file",
        "content": ["abcd", "efgh", "ijk"]
        }]
    },
    {"name":"data",
     "type":"directory",
     "children": [{
          "name":"data1.txt",
          "type":"file",
          "content": ["abcd", "xyz"]
        }]
    }
  ]
}

What I want to do is access the content of either of the two text files ( info1.txt or data1.txt ) dynamically and store them as a List of Strings. By dynamically I mean that the filename ie, the key ( info1.txt or data1.txt ) will be provided by the user.

I was able to parse and get the value in a static way by using org.json library like this:

         File file = new File(myfilepath/myjsonfile.json);
         String jsonContent = null;
         try {
             content = FileUtils.readFileToString(file, "utf-8");
         } catch (IOException e) {
             e.printStackTrace();
         }

         // Convert JSON string to JSONObject
         JSONObject jsonObj = new JSONObject(jsonContent);
         JSONArray children =  jsonObj.getJSONArray("children");
         System.out.println(children.toString());
         JSONObject  child0 = children.getJSONObject(0);
         System.out.println(child0.toString());
         // and so on...

However, I can't figure out how to make this dynamic and store the file contents as a List of Strings based on the user input of the filename.

Can someone please help me out?

EDIT: Clarified the question. myfilepath here refers to the file path of the JSON file ( myjsonfile.json ).

You need to loop through each object and see if name has the file you are looking for. I would recommend you use a more verbose JSON processing library such as Jackson or Gson to make things easier. However, given your current implementation, you want to do something along the lines of:

        if(jsonObj.has("children")) {
            JSONArray mainChild = jsonObj.getJSONArray("children");
            for(int i=0; i < mainChild.length(); i++) {
                if(((JSONObject)mainChild.get(i)).has("children")) {
                    JSONArray child = ((JSONObject)mainChild.get(i)).getJSONArray("children");
                    for(int j=0; j < child.length(); j++) {
                        JSONObject obj = child.getJSONObject(j);
                        if(obj.has("name") 
                                && fileNameToLookFor.equals(obj.getString("name"))) {
                            if(obj.has("content")) {
                                return obj.getJSONArray("content");
                            }
                            return null;
                        }
                    }
                }
            }
            return null;
        }

Implement your own tree-node-walker :

class NodeWalker {

    private final JSONObject object;

    public NodeWalker(JSONObject object) {
        this.object = object;
    }

    public List<String> findContentFor(String name) {
        LinkedList<JSONObject> queue = new LinkedList<>();
        queue.add(object);
        while (queue.size() > 0) {
            JSONObject next = queue.pop();
            Object fileName = next.get("name");
            final String contentName = "content";
            if (fileName != null && fileName.equals(name) && next.has(contentName)) {
                JSONArray content = next.getJSONArray(contentName);
                if (content == null) {
                    return Collections.emptyList();
                }
                List<String> result = new ArrayList<>();
                IntStream.range(0, content.length()).forEach(i -> result.add(content.getString(i)));
                return result;
            }
            final String childrenName = "children";
            if (next.has(childrenName)) {
                JSONArray array = next.getJSONArray(childrenName);
                IntStream.range(0, array.length()).forEach(i -> queue.add(array.getJSONObject(i)));
            }
        }

        return Collections.emptyList();
    }
}

Simple usage:

import org.json.JSONArray;
import org.json.JSONObject;

import java.io.File;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.IntStream;

public class ProfileApp {

    public static void main(String[] args) throws Exception {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();
        List<String> strings = Files.readAllLines(jsonFile.toPath());
        String json = String.join("", strings);

        JSONObject jsonObj = new JSONObject(json);

        NodeWalker nodeWalker = new NodeWalker(jsonObj);
        String[] files = {"info1.txt", "data1.txt", "data2.txt"};
        for (String file : files) {
            System.out.println(file + " contents -> " + nodeWalker.findContentFor(file));
        }
    }
}

prints:

info1.txt contents -> [abcd, efgh, ijk]
data1.txt contents -> [abcd, xyz]
data2.txt contents -> []

Gson

Much easier to use Gson library and class model of your JSON payload. Let's create a class:

class FileNode {

    private String name;
    private String type;
    private List<FileNode> children;
    private List<String> content;

    public List<String> findContent(String name) {
        LinkedList<FileNode> queue = new LinkedList<>();
        queue.add(this);
        while (queue.size() > 0) {
            FileNode next = queue.pop();
            if (next.name.equals(name)) {
                return next.content;
            }
            if (next.children != null) {
                queue.addAll(next.children);
            }
        }

        return Collections.emptyList();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public List<FileNode> getChildren() {
        return children;
    }

    public void setChildren(List<FileNode> children) {
        this.children = children;
    }

    public List<String> getContent() {
        return content;
    }

    public void setContent(List<String> content) {
        this.content = content;
    }

    @Override
    public String toString() {
        return "FileNode{" +
                "name='" + name + '\'' +
                ", type='" + type + '\'' +
                ", children=" + children +
                ", content=" + content +
                '}';
    }
}

Which we can use as below:

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.io.File;
import java.io.FileReader;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

public class GsonApp {

    public static void main(String[] args) throws Exception {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();

        Gson gson = new GsonBuilder()
                .setPrettyPrinting()
                .create();

        FileNode root = gson.fromJson(new FileReader(jsonFile), FileNode.class);
        String[] files = {"info1.txt", "data1.txt", "data2.txt"};
        for (String file : files) {
            System.out.println(file + " contents -> " + root.findContent(file));
        }
    }
}

Above code prints:

info1.txt contents -> [abcd, efgh, ijk]
data1.txt contents -> [abcd, xyz]
data2.txt contents -> []

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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