11

Given a structure like this:

{
  "nameOfObject": { "score": 100 },
  "anotherObject": { "score": 30 }
}

Is it possible to map this to:

class Container {
  Map<String, ScoreKeeper> scoreKeepers;
}

class ScoreKeeper {
  String name;
  int score;
}

So that you end up with the name property of the ScoreKeeper instances set to "nameOfObject" and "anotherObject", respectively?

Nick Spacek
  • 4,717
  • 4
  • 39
  • 42
  • 1
    You will probably need to create a intermediate object to serialize to , which in turn can be converted to the representation you want. – Bart Oct 08 '13 at 12:47

3 Answers3

4

I am a firm believer in separating your POJOs from externalization. Read your JSON into a Map and then build you Container/ScoreKeeper objects like this (apols for any typos):

mapper = new ObjectMapper();

Map<String,Object> data = mapper.readValue(inputstream, Map.class);

Container c = new Container();

for(Map.Entry<String, Object> me : data.entrySet()) {
    String key = me.getKey();
    Map info = (Map) me.getValue();

    ScoreKeeper sk = new ScoreKeeper();
    sk.setName(key);
    Integer q = info.get("score");
    sk.setScore(q);

    c.put(key, sk);
}
Buzz Moschetti
  • 7,057
  • 3
  • 23
  • 33
  • Sure. I was actually just looking for a succinct way to deserialize an existing API. Thanks for the suggestion though. – Nick Spacek Oct 08 '13 at 16:07
4

Alternative solution, where the key name is set on the value object by using a custom Deserializer:

@Test
public void test() throws JsonParseException, JsonMappingException, IOException {
    ObjectMapper mapper = new ObjectMapper();

    Data data = mapper.readValue("{\"users\": {\"John\": {\"id\": 20}, \"Pete\": {\"id\": 30}}}", Data.class);

    assertEquals(20, data.users.get("John").id);
    assertEquals(30, data.users.get("Pete").id);
    assertEquals("John", data.users.get("John").name);
    assertEquals("Pete", data.users.get("Pete").name);
}

public static class Data {
    @JsonDeserialize(contentUsing = Deser.class)
    public Map<String, User> users;
}

public static class User {
    public String name;
    public int id;
}

public static class Deser extends JsonDeserializer<User> {

    @Override
    public User deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        String name = ctxt.getParser().getCurrentName();

        User user = p.readValueAs(User.class);

        user.name = name;  // This copies the key name to the value object

        return user;
    }
}
john16384
  • 7,800
  • 2
  • 30
  • 44
  • I was not able to get this approach to work with the requested JSON (in the original question), this approach only works if there's a root node containing 'nameOfObject' and 'anotherObject' – Nick Nov 02 '17 at 05:18
0

Here's an improved version of @Buzz Moschetti's, it uses Jackson's ObjectMapper.convertValue() to handle parsing the properties

ObjectMapper mapper = new ObjectMapper();

Map<String,Object> data = mapper.readValue(inputstream, Map.class);

Container c = new Container();

for(Map.Entry<String, Object> entry : data.entrySet()) {
    String name = entry.getKey();
    ScoreKeeper sk = mapper.convertValue(entry.getValue(), ScoreKeeper.class);
    sk.name = name;
    c.scoreKeepers.put(name, sk);
}
Nick
  • 784
  • 8
  • 12