9

Is there a way to use inbuilt Jackson capabilities to convert a list of json object to HashMap using java

Explanation: Json structure that i need to parse

{
    list:[
        {
            keyId : 1,
            keyLabel : "Test 1",
            valueId: 34,
            valueLabel: "Test Lable"
        },
        {
            keyId : 2,
            keyLabel : "Test 2",
            valueId: 35,
            valueLabel: "Test Lable"
        },
        {
            keyId : 3,
            keyLabel : "Test 3",
            valueId: 36,
            valueLabel: "Test Lable"
        }
    ]
}

The object model I am expecting,

class Key{
    int keyId;
    String keyLable;

    hashCode(){
    return keyId.hashCode();
    }
}

class Value{
    int valueId;
    String valueLable;

    hashCode(){
    return valueId.hashCode();
    }
}

I need to convert the above json list to a map like this,

HashMap<Key,Value> map;
Ysak
  • 2,601
  • 6
  • 29
  • 53

4 Answers4

1

I would suggest to do it manually. You just have to write few lines. Something Like

    ObjectMapper jmap = new ObjectMapper();
    //Ignore value properties
    List<Key> keys = jmap.readValue("[{}, {}]", jmap.getTypeFactory().constructCollectionType(ArrayList.class, Key.class));
    //Ignore key properties
    List<Value> values = jmap.readValue("[{}, {}]", jmap.getTypeFactory().constructCollectionType(ArrayList.class, Value.class));

    Map<Key, Value> data = new HashMap<>();
    for (int i = 0; i < keys.size(); i++) {
        data.put(keys.get(i), values.get(i));
    }

Note: There is spell mismatch in your json and model (valueLabel != valueLable).

M Faisal Hameed
  • 673
  • 1
  • 7
  • 25
  • Then you can use custom deserializer. – M Faisal Hameed Mar 04 '16 at 06:24
  • Yeah...I am also looking towards writing one, But as @neal specified, is there a way to avoid using intermediate object....I couldn't find anyway without using the intermediate class... – Ysak Mar 04 '16 at 06:30
  • If I am getting it right, you want to avoid CustomObject. Your json representing an object, not list. So jackson need exact model to map json. However you can extract list from your json object and then user custom deserializer. `JsonNode node = mapper.readTree(json).path("list")` – M Faisal Hameed Mar 04 '16 at 07:57
0

Convert list to array of map first and then create map of key and value.

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

import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class JsonMapExample {

    public static void main(String[] args) {

        try {

            ObjectMapper mapper = new ObjectMapper();
            String json = "{\"list\":[{\"keyId\":1,\"keyLabel\":\"Test 1\",\"valueId\":34,\"valueLabel\":\"Test Lable\"},{\"keyId\":2,\"keyLabel\":\"Test 2\",\"valueId\":35,\"valueLabel\":\"Test Lable\"},{\"keyId\":3,\"keyLabel\":\"Test 3\",\"valueId\":36,\"valueLabel\":\"Test Lable\"}]}";
            JsonNode node = mapper.readTree(json).path("list");
            JsonParser parser = mapper.treeAsTokens(node);
            Map<String, Object>[] clients = parser.readValueAs(new TypeReference<Map<String, Object>[]>() {
            });
            Map<Key, Value> result = new HashMap<Key, Value>();
            for (Map<String, Object> map : clients) {
                int keyId = Integer.parseInt(map.get("keyId").toString());
                int valueId = Integer.parseInt(map.get("valueId").toString());
                result.put(new Key(keyId, map.get("keyLabel").toString()),
                        new Value(valueId, map.get("valueLabel").toString()));
            }
            System.out.println(result);
        } catch (JsonGenerationException e) {
            e.printStackTrace();
        } catch (JsonMappingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
Dipali Vasani
  • 2,526
  • 2
  • 16
  • 30
0

I think you'd have to write a custom deserializer. Here's an option:

    public class CustomObject{
    private List<KeyValuePairs> list;

    public List<KeyValuePairs> getList() {
        return list;
    }
    public void setList(List<KeyValuePairs> list) {
        this.list = list;
    }

    //Helper
    public HashMap<CustomKey,CustomValue> getCustomKeyValueMap(){
        Map<CustomKey,CustomValue> map = new HashMap<CustomKey,CustomValue>();
        for(KeyValuePairs kvpair: list){
            map.put(new CustomKey(kvpair.getKeyId(),kvpair.getKeyLabel()), 
                    new CustomValue(kvpair.getValueId(),kvpair.getValueLabel()));
        }
        return map;
    }
}

    public class KeyValuePairs{
        private int keyId;
        private String keyLabel;
        private int valueId;
        private String valueLabel;

        //Getters and Setters
        public int getKeyId() {
            return keyId;
        }
        public void setKeyId(int keyId) {
            this.keyId = keyId;
        }
        public String getKeyLabel() {
            return keyLabel;
        }
        public void setKeyLabel(String keyLabel) {
            this.keyLabel = keyLabel;
        }
        public int getValueId() {
            return valueId;
        }
        public void setValueId(int valueId) {
            this.valueId = valueId;
        }
        public String getValueLabel() {
            return valueLabel;
        }
        public void setValueLabel(String valueLabel) {
            this.valueLabel = valueLabel;
        }

    }
        public class CustomKey{
            int keyId;
            String keyLable;

            public CustomKey(){

            }

            public CustomKey(int keyId, String keyLable){
                this.keyId = keyId;
                this.keyLable = keyLable;
            }

            public int getKeyId() {
                return keyId;
            }
            public void setKeyId(int keyId) {
                this.keyId = keyId;
            }
            public String getKeyLable() {
                return keyLable;
            }
            public void setKeyLable(String keyLable) {
                this.keyLable = keyLable;
            }

        }       



        public class CustomValue{
            int valueId;
            String valueLable;

            public CustomValue(){

            }
            public CustomValue(int valueId, String valueLable){
                this.valueId = valueId;
                this.valueLable = valueLable;
            }

            public int getValueId() {
                return valueId;
            }
            public void setValueId(int valueId) {
                this.valueId = valueId;
            }
            public String getValueLable() {
                return valueLable;
            }
            public void setValueLable(String valueLable) {
                this.valueLable = valueLable;
            }

        }
neal
  • 880
  • 6
  • 11
  • Can we avoid using KeyValuePairs ?? – Ysak Mar 04 '16 at 06:25
  • I don't think you can avoid using that. I believe Jackson needs that class to figure out how to map the fields through reflection. All you would do to get the result would be something like: CustomObject o = objectMapper.readValue(carJson, CustomObject.class); Map map = o.getCustomKeyValueMap(); – neal Mar 04 '16 at 07:30
0

This answer may not be what you are looking for, certainly not a simple solution. This helper class reads an input file like it was SAX-XML parser, unlike DOM-XML parser.

My usecase is a large "randomly" structured json files and update database objects. This never loads entire document to a RAM memory. I cannot use a strongly typed objects so went for hashmap solution.

This one loops input file, collects field values per given breakpoint object path, calls a handler function. Hashmap has parent.entry.field=xxxxx values, dump keyval pairs to see a naming syntax.

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Stack;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;

public class JSONHandler {
    private Stack<String> parents;
    private String cachedParent;
    private Map<String,String> entry;
    private Map<String,String> parentEntry;

    public JSONHandler(JSONHandler.EntryHandler handler, JsonParser jsonP, 
                        String path) throws Exception {
        parents = new Stack<String>();
        entry = new LinkedHashMap<String,String>(16);// new HashMap<String,String>(16);
        parentEntry = new LinkedHashMap<String,String>(8);

        if (!path.endsWith(".")) path+=".";

        boolean isParent=true;
        int arrayIdx=-1;
        cachedParent="";
        JsonToken token;
        boolean doProcess=true;
        while ( (token=jsonP.nextToken()) != null) {
            String name = jsonP.getCurrentName();
            if (token == JsonToken.START_OBJECT) {
                parents.push(name);
                if (cachedParent.equals(path)) {
                    entry.clear(); // start new target entry
                    arrayIdx=0;
                    if (!parentEntry.isEmpty()) entry.putAll(parentEntry);
                    isParent=false;
                } else if (!cachedParent.startsWith(path)) {
                    isParent=true; // add fields to parent entry
                }
                cachedParent = implodeStack(parents);
            } else if (token == JsonToken.END_OBJECT) {
                parents.pop();
                cachedParent = implodeStack(parents);
                if (cachedParent.equals(path)) {
                    doProcess=handler.process(entry);
                    arrayIdx=-1;
                    if (!doProcess) break;
                } else if (name==null && cachedParent.startsWith(path)) {
                    String sArrayIdx = parents.peek(); // increment arrayIndex+1
                    parents.set(parents.size()-1, ""+(Integer.parseInt(sArrayIdx)+1) );
                    cachedParent = implodeStack(parents);
                } else if (!cachedParent.startsWith(path)) {
                    Iterator<Map.Entry<String,String>> iter=parentEntry.entrySet().iterator();
                    while(iter.hasNext()) {
                        Map.Entry<String,String> me = iter.next();
                        if (me.getKey().startsWith(cachedParent))
                            iter.remove();
                    }
                }
            } else if (token == JsonToken.START_ARRAY) {
                parents.push(name);
                if (arrayIdx>-1) parents.push(String.valueOf(arrayIdx));
                cachedParent = implodeStack(parents);
            } else if (token == JsonToken.END_ARRAY) {
                parents.pop();
                if (arrayIdx>-1) {
                    parents.pop();
                    arrayIdx=0;
                }
                cachedParent = implodeStack(parents);
            } else if (token == JsonToken.FIELD_NAME) {
                //System.out.println("field="+jsonP.getCurrentName());
            } else {
                String value;
                if (token == JsonToken.VALUE_NULL) value = null;
                else if (token == JsonToken.VALUE_TRUE) value = "1";
                else if (token == JsonToken.VALUE_FALSE) value = "0";
                else value = jsonP.getText();
                if (cachedParent.startsWith(path)) {
                    if (name==null && arrayIdx>-1) {
                        // simple array "values":["aa","bb","cc"], 
                        // write parent.item.values.0=aa, .1=bb, .2=cc
                        parents.set(parents.size()-1, ""+(arrayIdx++) );
                        cachedParent = implodeStack(parents);
                        entry.put(cachedParent.substring(0,cachedParent.length()-1), value);
                    } else
                        entry.put(cachedParent+name, value);
                } else if (isParent) {
                    parentEntry.put(cachedParent+name, value);
                }
            }
        }
    }

    private String implodeStack(Stack<String> stack) {
        StringBuilder sb = new StringBuilder();
        for(String value : stack) {
            if (value!=null)
                sb.append(value + ".");
        }
        return sb.toString();
    }


    public static interface EntryHandler {
        public void startDocument() throws Exception;
        public boolean process(Map<String,String> entry) throws Exception;
    }

}

Example client

    JSONHandler.EntryHandler handler = new JSONHandler.EntryHandler() {
        public void startDocument() throws Exception {};
        public boolean process(Map<String,String> entry) throws Exception {
            for(String key : entry.keySet())
                System.out.println(key+"="+entry.get(key));
            return true;
        }
    };

    JsonFactory jsonF = new JsonFactory();
    jsonF.enable(JsonParser.Feature.AUTO_CLOSE_SOURCE);
    JsonParser jsonP = jsonF.createParser(file);
    try {
       handler.startDocument();
       new JSONHandler(handler, jsonP, "list");
    } finally {
       jsonP.close();
    }

(copypaste simplifying from my application could have introduce syntax errors)

Whome
  • 10,181
  • 6
  • 53
  • 65