I have JSON response:
{
"part1.id": "1",
"part1.name": "Name1",
"part2.id": "2",
"part2.name": "Name2"
}
POJO / DTO class for 1 user:
public class User {
private String id;
private String name;
// set/get
}
And class for the whole response:
public class UsersResponse {
private List<User> users;
// set/get
}
I can retrieve values in Map and then parse/map in code manually like Jackson JSON map key as property of contained object
Also there is @JsonAlias for multiple naming variations but it maps to one object.
Is there any other way to map/group JSON values to List for provided prefixes?
There is no already implemented annotations which allow to do it by configuration. You need to implement deserialiser. Simple version ot that deserialiser could look like below:
class UsersResponseDeserializer extends JsonDeserializer<UsersResponse> {
private Pattern propertyPattern = Pattern.compile("^part(\\d+)\\.(.+)$");
@Override
public UsersResponse deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
Integer lastIndex = null;
String lastName = null;
Map<Integer, Map<String, String>> users = new LinkedHashMap<>();
while (p.currentToken() != null) {
switch (p.currentToken()) {
case FIELD_NAME:
String name = p.getText();
Matcher matcher = propertyPattern.matcher(name);
if (matcher.matches()) {
lastIndex = Integer.parseInt(matcher.group(1));
lastName = matcher.group(2);
}
break;
case VALUE_STRING:
if (lastIndex != null && lastName != null) {
Map<String, String> user = users.computeIfAbsent(lastIndex, k -> new HashMap<>());
user.put(lastName, p.getValueAsString());
lastIndex = null;
lastName = null;
}
break;
default:
break;
}
p.nextToken();
}
UsersResponse response = new UsersResponse();
response.setUsers(users);
return response;
}
}
I changed a little bit UsersResponse
and it looks like below:
@JsonDeserialize(using = UsersResponseDeserializer.class)
class UsersResponse {
private Map<Integer, Map<String, String>> users;
public Map<Integer, Map<String, String>> getUsers() {
return users;
}
public void setUsers(Map<Integer, Map<String, String>> users) {
this.users = users;
}
@Override
public String toString() {
return "UsersResponse{" +
"users=" + users +
'}';
}
}
Example usage:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.type.CollectionLikeType;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class JsonApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("path to json").getAbsoluteFile();
ObjectMapper mapper = new ObjectMapper();
UsersResponse data = mapper.readValue(jsonFile, UsersResponse.class);
System.out.println(data);
CollectionLikeType usersListType = mapper.getTypeFactory().constructCollectionLikeType(List.class, User.class);
List<User> users = mapper.convertValue(data.getUsers().values(), usersListType);
System.out.println(users);
}
}
Abobe app for given JSON
:
{
"part1.id": "1",
"part1.name": "Name1",
"part2.id": "2",
"part2.name": "Name2",
"part33.id": "33",
"part33.name": "Name33"
}
Prints:
UsersResponse{users={1={name=Name1, id=1}, 2={name=Name2, id=2}, 33={name=Name33, id=33}}}
[User{id='1', name='Name1'}, User{id='2', name='Name2'}, User{id='33', name='Name33'}]
In deserialiser I used Map<Integer, Map<String, String>>
because I did not want to play with matching POJO
properties
and JSON
keys.