1

I have a table that contains province, districts and values within a district

DATA

+------+--------+---------------------+------------+
| ID   |Province| District            | Codes      |
+------+--------+---------------------+------------+
| 1001 | Texas  | 1st                 | 1054       |
| 1002 | Texas  | 2nd                 | 1055       |
| 1003 | Ohio   | 1st                 | 3045       |
| 1004 | Ohio   | 2nd                 | 3046       |
| 1005 | Utah   | 1st                 | 1023       |
| 1006 | Utah   | 2nd                 | 1024       |
| 1007 | Utah   | 3rd                 | 1025       |
+------+--------+---------------------+------------+

I want to provide a response when users hits the end point

{
  "country": "USA",
  "listing": {
    "Texas": {
      "1st": {
        "1054": "",
        "1055": "",
      },
      "2nd": {
        "1056": "",
        "1057": "",
      },
    },
    "Ohio": {
      "1st": {
        "3045": "",
        "3128": "",
      },
      "2nd": {
        "3046": ""
      },
    },
    "Utah": {
      "1st": {
        "1023": "",
      },
      "2nd": {
        "1024": ""
      },
      "3rd": {
        "1025": ""
      },
    },
  }
}

I have chosen a Map object to generate the final result.

@RequestMapping(path = "/getData/country", method = RequestMethod.GET)
public ResponseEntity<Map<String, Object>> retrieveData() {
    Iterable<CountryModel> stud = myDataRepo.findAll();

    Map<String, Object> parent = new HashMap<>();
    parent.put("country", "USA");

    stud.forEach(d -> {
        String r = d.getYPROVINCEN();

        Map<String, String> child = new HashMap<>();
        child.put("name", d.getYDISTRICTN());

        if (parent.containsKey(r)) {
            List<Map<String, String>> children =
                    (List<Map<String, String>>) parent.get(r);
            children.add(child);
        } else {
            List<Map<String, String>> children = new ArrayList<>();
            children.add(child);
            parent.put(r, children);
        }
    });

    return ResponseEntity.ok().body(parent);
}

My Model

@Entity
@Table(name = "Country")
public class CountryModel {
    private String Province;
    private String District;
    private String Codes;
    //getters and setters

My Repository

@Repository
public interface MyDataRepo extends CrudRepository<CountryModel, String> {
}

From my code above I have managed to go only 1 step and extract the province and the districts

{
  "country": "USA",
  "Texas": [
    {
      "name": "1st"
    },
    {
      "name": "2nd"
    },
  ],
  "Ohio": [
    {
      "name": "1st"
    },
    {
      "name": "2nd"
    },
  ],
  "Utah": [
    {
      "name": "1st"
    },
    {
      "name": "2nd"
    },
    {
      "name": "3rd"
    },
  ],
}

How can I set up my function to generate the nested JSON above and get the codes, district and province?

arriff
  • 399
  • 1
  • 10
  • 30

1 Answers1

3

Since I couldn't reproduce the database. I made some tweaks to answer the question.

public class TestClass {

    public static void main(String[] args) throws Exception {
        // dummy database table
        List<CountryModel> modelTable = new ArrayList<>();
        modelTable.add(new CountryModel("1001", "Texas", "1st", "1054"));
        modelTable.add(new CountryModel("1002", "Texas", "2nd", "1055"));
        modelTable.add(new CountryModel("1003", "Ohio", "1st", "3045"));
        modelTable.add(new CountryModel("1004", "Ohio", "2nd", "3046"));
        modelTable.add(new CountryModel("1005", "Utah", "1st", "1023"));
        modelTable.add(new CountryModel("1006", "Utah", "2nd", "1024"));
        modelTable.add(new CountryModel("1007", "Utah", "3rd", "1025"));
        modelTable.add(new CountryModel("1008", "Utah", "3rd", "1026"));
        System.out.println(retrieveData(modelTable).toString());
    }

    private static Map<String, Object> retrieveData(List<CountryModel> modelTable) {
        Map<String, Object> parent = new HashMap<>();
        parent.put("country", "USA");
        // inserting the listings entry
        parent.put("listing", new HashMap<String, Object>());
        @SuppressWarnings("unchecked")
        final Map<String, Object> listings = (Map<String, Object>) parent.get("listing");

        modelTable.forEach(entry -> {
            String province = entry.getProvince();

            if (!listings.containsKey(province)) {
                listings.put(province, new HashMap<String, Object>());
            }

            @SuppressWarnings("unchecked")
            Map<String, Object> insideProvince = (Map<String, Object>) listings.get(province);

            String district = entry.getDistrict();

            // Since Java 9
            // insideProvince.put(district, Map.of(entry.getCode(), ""));
            
            // Before Java 9
            // Map<String, String> codeMap = new HashMap<>();
            // codeMap.put(entry.getCode(), "");
            // insideProvince.put(district, codeMap);
            
            if (!insideProvince.containsKey(district)) {
                insideProvince.put(district, new HashMap<String, Object>());
            }

            @SuppressWarnings("unchecked")
            Map<String, Object> insideDistrict = (Map<String, Object>) insideProvince.get(district);
            insideDistrict.put(entry.code, "");
        });

        return parent;
    }

    static class CountryModel {
        private String id;
        private String province;
        private String district;
        private String code;

        CountryModel(String id, String province, String district, String code) {
            this.id = id;
            this.province = province;
            this.district = district;
            this.code = code;
        }
        // getters and setters
    }
}

The solution is pretty straightforward. The place where you missed it was you should have used HashMaps instead of a ArrayList.

dodobird
  • 168
  • 11
  • Hi @dodobird I get this error `Cannot resolve method 'of' in 'Map'` on this line `insideProvince.put(district, Map.of(entry.getCode(), ""));`, – arriff Jun 23 '21 at 05:32
  • Sorry I made use of `Java 11` to answer the question. [Map.of()](https://docs.oracle.com/javase/9/docs/api/java/util/Map.html#of--) was introduced after `Java 9`. Since you are using `Java 8`, you may use: `Map codeMap = new HashMap<>();` `insideProvince.put(district, codeMap);` – dodobird Jun 23 '21 at 06:18
  • Hi @dodobird I cant seem to extact the values using the example above inside the district – arriff Jun 23 '21 at 06:26
  • Are you talking about deserializing the `Map` instance after it has been returned by `retrieveData()`? – dodobird Jun 23 '21 at 06:34
  • From this bit `Map codeMap = new HashMap<>(); insideProvince.put(district, codeMap);` I can get the province and the district but the codes are not being shown inside the individual Districts – arriff Jun 23 '21 at 06:38
  • Sorry I forgot to mention the `PUT` operation that needs to be performed. So finally it would be:`Map codeMap = new HashMap<>();` `codeMap.put(entry.getCode(), "");` `insideProvince.put(district, codeMap);`. I have updated the answer. – dodobird Jun 23 '21 at 06:47
  • Thank you so much, One more question and if a district has more than one code, how can I map it tp show all instead of one? – arriff Jun 23 '21 at 06:51
  • I have updated the answer again. You will basically check whether `district` is already present or not. If present you will not add a new `Map`, instead you will add the `code` to the `Map`. – dodobird Jun 23 '21 at 07:08