1

I am beginner to core Java. I am fetching data from sql server using JDBC template and joins. Now I want to group received data.

processId  processName  subId  subName  valueId value gId  gradeName
1           p1           1      s1       11      v1    1     g1
1           p1           1      s1       11      v1    2     g2
2           p2           2      s2       null    null  null null
3           p3           3      s3       13      v3    null null

And I want following output:

[{
    "processId": 1,
    "processname": "p1",
    "sub": [{
        "subId": 11,
        "subName": "s1",
        "value": [{
            "valueId": 11,
            "value": "v1",
            "grades": [{
                "gId": 1,
                "gradeName": "g1"
            }, {
                "gId": 2,
                "gradeName": "g2"
            }]
        }]
    }]
}, {
    "processId": 2,
    "processname": "p2",
    "sub": [{
        "subId": 12,
        "subName": "s2",
        "value": []
    }]
}, {
    "processId": 3,
    "processname": "p3",
    "sub": [{
        "subId": 13,
        "subName": "s3",
        "value": [{
            "valueId": 3,
            "value": "g3",
            "grade": []
        }]
    }]
}]

I found similar question here: link

I used while loop to iterate over output as mentioned in the linked question, but I am unable to do so.

One of user states in comment that use hashmap But I am not able to implement hashmap. Can anyone guide me about it?

Can anyone guide/help me to sort this out.

1 Answer 1

1

There are many ways to do that, one could be the following.

First, create a model to structure your result:

package core.map;

import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.databind.annotation.JsonSerialize;

public class Process {
    private String processId;
    private String processName;

    @JsonSerialize(using = MapAsListSerializer.class)
    private Map<String, Sub> sub = new HashMap<>();

    public Process(final String processId, final String processName) {
        this.processId = processId;
        this.processName = processName;
    }

    public String getProcessId() {
        return processId;
    }

    public String getProcessName() {
        return processName;
    }

    public Map<String, Sub> getSub() {
        return sub;
    }

    static class Sub {
        private String subId;
        private String subName;

        @JsonSerialize(using = MapAsListSerializer.class)
        private Map<String, Value> value = new HashMap<>();

        public Sub(final String subId, final String subName) {
            this.subId = subId;
            this.subName = subName;
        }

        public String getSubId() {
            return subId;
        }

        public String getSubName() {
            return subName;
        }

        public Map<String, Value> getValue() {
            return value;
        }

        static class Value {
            private String valueId;
            private String value;

            @JsonSerialize(using = MapAsListSerializer.class)
            private Map<String, Grade> grades = new HashMap<>();

            public Value(final String valueId, final String value) {
                this.valueId = valueId;
                this.value = value;
            }

            public String getValueId() {
                return valueId;
            }

            public String getValue() {
                return value;
            }

            public Map<String, Grade> getGrades() {
                return grades;
            }

            static class Grade {
                private String gId;
                private String gradeName;

                public Grade(final String gId, final String gradeName) {
                    this.gId = gId;
                    this.gradeName = gradeName;
                }

                public String getgId() {
                    return gId;
                }

                public String getGradeName() {
                    return gradeName;
                }

            }
        }
    }
}

This was just a bunch of (more or less) simple POJOs, except the line:

@JsonSerialize(using = MapAsListSerializer.class)

The MapAsListSerializer is just a tiny custom Json Serializer to display the final result as needed (flat list instead of a map).

package core.map;

import java.io.IOException;
import java.util.Map;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

public class MapAsListSerializer extends JsonSerializer<Map<?, ?>> {

    @Override
    public void serialize(Map<?, ?> incomingMap, JsonGenerator generator, SerializerProvider arg2)
            throws IOException, JsonProcessingException {
        generator.writeObject(incomingMap.values());

    }
}

At least we need the code to compute the (fake database) result, this could be something like:

package core.map;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import core.map.Process.Sub;
import core.map.Process.Sub.Value;
import core.map.Process.Sub.Value.Grade;

public class ResultMapper {

    private final static ObjectMapper mapper = new ObjectMapper();

    private final static List<List<String>> resultSet = new ArrayList<>();

    private final static Map<String, Process> processes = new HashMap<>();

    static {
        resultSet.add(Arrays.asList("1", "p1", "1", "s1", "11", "v1", "1", "g1"));
        resultSet.add(Arrays.asList("1", "p1", "1", "s1", "11", "v1", "2", "g2"));
        resultSet.add(Arrays.asList("2", "p2", "2", "s2", null, null, null, null));
        resultSet.add(Arrays.asList("3", "p3", "3", "s3", "13", "v3", null, null));
    }

    public static void main(String[] args) throws JsonProcessingException {
        resultSet.forEach(row -> rowToProcess(row, processes));
        System.out.println(mapper.writeValueAsString(processes.values()));
    }

    private static void rowToProcess(final List<String> row, final Map<String, Process> processes) {

        final String processId = row.get(0);
        final String processName = row.get(1);
        final String subId = row.get(2);
        final String subName = row.get(3);
        final String valueId = row.get(4);
        final String value = row.get(5);
        final String gId = row.get(6);
        final String gradeName = row.get(7);

        Process currentProcess = processes.get(processId);
        if (currentProcess == null) {
            currentProcess = new Process(processId, processName);
            processes.put(processId, currentProcess);
        }

        Map<String, Sub> subs = currentProcess.getSub();
        Sub currentSub = subs.get(subId);
        if (currentSub == null) {
            currentSub = new Process.Sub(subId, subName);
            subs.put(subId, currentSub);
        }

        Map<String, Value> values = currentSub.getValue();
        if (valueId == null)
            return;
        Value currentValue = values.get(valueId);
        if (currentValue == null) {
            currentValue = new Sub.Value(valueId, value);
            values.put(valueId, currentValue);
        }

        if (gId == null)
            return;
        Map<String, Grade> grades = currentValue.getGrades();
        grades.put(gId, new Value.Grade(gId, gradeName));
    }
}

So nearly everything is out of the JDK (1.8), except the Json dependencies. All needed extra stuff is available here

Extension:

Of course it's possible, you can omit the getters which are just necessary for Jackson processing. That would result in something like (without getters):

package core.map;

import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.databind.annotation.JsonSerialize;

public class Process {
    private String processId;
    private String processName;

    @JsonSerialize(using = MapAsListSerializer.class)
    private Map<String, Sub> sub = new HashMap<>();

    public Process(final String processId, final String processName) {
        this.processId = processId;
        this.processName = processName;
    }

    public Map<String, Sub> getSub() {
        return sub;
    }

    static class Sub {
        private String subId;
        private String subName;

        @JsonSerialize(using = MapAsListSerializer.class)
        private Map<String, Value> value = new HashMap<>();

        public Sub(final String subId, final String subName) {
            this.subId = subId;
            this.subName = subName;
        }

        public Map<String, Value> getValue() {
            return value;
        }

        static class Value {
            private String valueId;
            private String value;

            @JsonSerialize(using = MapAsListSerializer.class)
            private Map<String, Grade> grades = new HashMap<>();

            public Value(final String valueId, final String value) {
                this.valueId = valueId;
                this.value = value;
            }

            public Map<String, Grade> getGrades() {
                return grades;
            }

            static class Grade {
                private String gId;
                private String gradeName;

                public Grade(final String gId, final String gradeName) {
                    this.gId = gId;
                    this.gradeName = gradeName;
                }

            }
        }
    }
}

But then you have to tell Jackson to use the fields directly instead of the getters:

mapper.setVisibility(PropertyAccessor.GETTER, Visibility.NONE);
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);

I will keep both solutions here because that's something about personal preference. And of course you can omit the Process class in general and write another one similar to that, but somehow the data has to be structured.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for answer. Is it possible without using getter/setters. I don't want to use processes class that u used.
Adjusted my answer regarding your question.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.