How to serialize a part of json

130 Views Asked by At

I have a json file with users, each users has email and password and corresponding POJO exist to deserialize them.

I'm using both jackson and JSON.simple.

However, I want to add users at will, and create a method to find them by their description, let's say this is my json:

{
  "user1": {
    "email": "[email protected]",
    "password": "qwe123",
  },
  "user2": {
    "email": "[email protected]",
    "password": "abc123",
  },

...
  "userX": {
    "email": "[email protected]",
    "password": "omg123",
  }
}

This is my POJO:

public record User(String email, String password) {}

I dont want to create superPOJO and add each user as I create them.

I would like to create method that would read my json, and returned the User object based on String input.

For now I created users in array and get them using their index, but now situation requires giving users "nicknames" and getting them by their nickname.

Now I am keeping user like this:

[
  {
    "email": "[email protected]",
    "password": "xxx111",
  },
  {
    "email": "[email protected]",
    "password": "yyy222",
  }
]

This is my current method:

public User getUser(int index) throws IOException {
    return Parser.deserializeJson(getUserFile(), User[].class)[index];
}

where Parser#deserializeJson() is:

public <T> T deserializeJson(String fileName, Class<T> clazz) throws IOException {
    return new ObjectMapper().readValue(Utils.reader(fileName), clazz);
}

And Utils.reader just brings file from the classpath.

I would like a method like this:

public User getUser(String nickname) throws IOException {
    return Parser.deserializeJson(getUserFile(), User.class);
}

and when calling this method with parameter nickname of user2 I'd get User object with fields: email [email protected] and password abc123

2

There are 2 best solutions below

3
Garrett Motzner On BEST ANSWER

Given your input json is an object (map) of User-like objects:

{
  "user1": {
    "email": "[email protected]",
    "password": "qwe123",
  },
  "user2": {
    "email": "[email protected]",
    "password": "abc123",
  },

...
  "userX": {
    "email": "[email protected]",
    "password": "omg123",
  }
}

then you should be able to deserialize it into a (Hash)Map of Users:

public <T> Map<String, Class<T>> deserializeJson(String fileName) throws IOException {
    return new ObjectMapper().readValue(Utils.reader(fileName), new TypeReference<HashMap<String, Class<T>>>(){});
}

and then use it like so:

public User getPredefinedUser(String nickname) throws IOException {
    return Parser.deserializeJson(getUserFile(), User.class).get(nickname);
}

(Although you probably want to parse once and store that somewhere, not parse every time).

Thanks for the compile check and fix by hetacz. Had the templates wrong, and an unnecessary (for this case) class variable

2
hetacz On

After putting some thought into my own problem I decided to do it like this (I also dropped using a JSON.simple library in the meantime, as per suggestion that using two for such a simple task is an overkill).

I am using com.fasterxml.jackson.core - Jackson Databind.

I used helper function from Parser class where MAPPER is a new ObjectMapper():

    public JsonNode toJsonNode(String fileName) throws IOException {
        return MAPPER.readTree(Utils.reader(fileName));
    }

here is also a parseJson method from Parser class:

    public <T> T parseJson(String json, Class<T> clazz) throws JsonProcessingException {
        return MAPPER.readValue(json, clazz);
    }

I wrapped it all together in method I wanted (that searches by nickname) and everything works fine, I get desired User object in the end:

    public User getUser(String nickname) throws IOException {
        return Parser.parseJson(Parser.toJsonNode(USERS).get(nickname).toString(), User.class);
    }

And now the test:

    @Test(groups = Group.TEST)
    public void jsonTest() throws IOException, ParseException {
        User user = Utils.getUser("user2");
        log.warn(user.email());
        log.warn(user.password());
    }

output is:

2022-11-23 T 02:47:58.177+0100 [28][WARN] tests.smoke.SmokeTest->jsonTest [email protected]
2022-11-23 T 02:47:58.177+0100 [28][WARN] tests.smoke.SmokeTest->jsonTest abc123

On the side note I think I will switch to one JSON library in my whole projects, having two of them is unnecessairly causing mess.

Other thing I found important is, when getting to the value behind a key, use asText() method not toString(), as toString() returned text in double quotes! This is actual code fragment from my automation framework:

expected:<"["This field is required"]"> but was:<"[This field is required]">

So it basically put quotes over quotes, that every string in JSON has to be wrapped in anyway...