2

I know there is a lot of content on this throughout the web but I couldn't find one that will help me solve this (been looking for about two days now...).

I have a json string which looks like this:

[
  {
    "firstName": "dan",
    "lastName": "cohen",
    "email": "dann@gmail.com",
    "userName": "aaaddd",
    "password": "1cczzdddds",
    "agentCode": 0
  },
  {
    "firstName": "omer",
    "lastName": "sha",
    "email": "superomsha@gmail.com",
    "userName": "asdf",
    "password": "asdf",
    "agentCode": 1
  }
]

I am trying to read this json into Set<T> since I am going to use this function in order to read json files to set of other types. E.g, here I am trying to read into Set<Agent> while later on I'll try to read another file into Set<Passenger>.

This is the function which reads the json file:

public Set<T> read() {
    try {
        return new ObjectMapper().readValue(new File(this.fileName), new TypeReference<Set<Agent>>(){});
    }

    /* Errors handling */
    } catch (IOException e) {
        e.printStackTrace();
    }

    return null;
}

The line Set<T> sample = new ObjectMapper().readValue(jsonString, new TypeReference<Set<Agent>>(){}); is responsible for deserialize the json string into a Set<Agent>.

But the moment I try to change it from:

Set<T> sample = new ObjectMapper().readValue(jsonString, new TypeReference<Set<Agent>>(){});

To:

Set<T> sample = new ObjectMapper().readValue(jsonString, new TypeReference<Set<T>>(){});

When trying to execute this code (which lies in the main function):

Set<Agent> agents = fileManagerAgent.read();
Iterator<Agent> it = agents.iterator();
while(it.hasNext()){
    System.out.println(it.next().getUserName());
}

I am getting an error saying:

Exception in thread "main" java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class model.objects.Agent (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; model.objects.Agent is in unnamed module of loader 'app')

From what I have read, this happens because in run-time, something happens to T and the compiler addresses it as Object and so, it is deserializing the json into a LinkedHashMap.

Once it does that, I cannot access the Agent object inside the set.

This is what calls the function to read from a json file into Set<T> (which is also in the main function):

FileManager<Agent> fileManagerAgent = new FileManager<>("src/data/agents.json");
Set<Agent> agents = fileManagerAgent.read();

Also, this code is written using Jackson.

Any ideas on how to solve this?

If any more information is needed please inform me and I will update the question.

Omer
  • 57
  • 1
  • 7
  • Perhaps create your own Set class (simple extension) and then use (this answer)[https://stackoverflow.com/questions/35778682/how-can-i-get-jackson-to-deserialize-into-my-own-array-implementation]?. – Arcanefoam Jun 23 '20 at 10:35
  • Yeah that won't work because as you already mentioned T isn't known at run time, but TypeReference needs to know it's type at run time or it can't do it's magic. How do you know you need a set of agent or passenger? – magicmn Jun 23 '20 at 10:36
  • Small addition not directly tied to your question. Currently you are reading the whole JSON as a String and then pass the String to object mapper. Try passing the file directly to object mapper. Reduces memory consumption and you have less lines of code. – magicmn Jun 23 '20 at 10:44
  • @Arcanefoam I will try the solution you linked to. – Omer Jun 23 '20 at 10:46
  • @magicmn I have added in the question the lines calling to the functions. – Omer Jun 23 '20 at 10:46
  • @magicmn I also editted the code to what you suggested and I have editted the question – Omer Jun 23 '20 at 10:48
  • 1
    Pass the Agent.class or Passenger.class to the filereader either when you construct it or when you call read. If you know the class it's very simple: https://stackoverflow.com/a/6349488/9712270 – magicmn Jun 23 '20 at 10:52
  • @magicmn I do not understand how to implement it – Omer Jun 23 '20 at 11:01

2 Answers2

2

Add the Class you want as parameter. In this example the passed Class has to be Agent.class for a FileManager<Agent>.

public Set<T> read(Class<T> clazz) {
    try {
        ObjectMapper = mapper = new ObjectMapper();
        return mapper.readValue(new File(fileName), mapper.getTypeFactory().constructCollectionType(Set.class, clazz));
    } catch (IOException e) {
        // Exception handling
    }
}

And then use it like this: Set<Agent> agents = new FileManager<>("agent.json").read(Agent.class);

or pass the class when you create youre file manager

public class FileManager<T> {

    private String fileName;
    private Class<T> clazz;

    public Set<T> read() {
        // Same code.
    }
}

Now the code would change to: Set<Agent> agents = new FileManager<>("agent.json", Agent.class).read();

magicmn
  • 1,787
  • 7
  • 15
  • I have just now solved it, using the same principle you suggested and came here to post it, it is almost the same as you described in this answer so I will mark it as the solution. Thanks for the help! – Omer Jun 23 '20 at 11:35
-1

Create VO class which is equivalent to your input file, pass this class to ObjectMapper.

new ObjectMapper().readValue(new File(this.fileName), Object of JsonSetVo);

public class JsonSetVo<T> {

    Set<T> inputSet;

    public Set<T> getInputSet() {
        return inputSet;
    }

    public void setInputSet(Set<T> inputSet) {
        this.inputSet = inputSet;
    }
}