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": "[email protected]",
    "userName": "aaaddd",
    "password": "1cczzdddds",
    "agentCode": 0
  },
  {
    "firstName": "omer",
    "lastName": "sha",
    "email": "[email protected]",
    "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.

8
  • Perhaps create your own Set class (simple extension) and then use (this answer)[stackoverflow.com/questions/35778682/…?. Commented Jun 23, 2020 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? Commented Jun 23, 2020 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. Commented Jun 23, 2020 at 10:44
  • @Arcanefoam I will try the solution you linked to. Commented Jun 23, 2020 at 10:46
  • 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: stackoverflow.com/a/6349488/9712270 Commented Jun 23, 2020 at 10:52

2 Answers 2

3

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();

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

1 Comment

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!
-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;
    }
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.