0

I have a json string in jsonString field and i am using ObjectMapper.

String jsonString = {"age":12,"location":"UK","gender":"male"};

ObjectMapper mapper = new ObjectMapper();
Map map = mapper.readValue(jsonString, Map.class);

Is there a way to retrieve field type from Map rather than using Pojo class. For example, age is of type integer, gender is of type string which does not involve using Pojo class but rather using Map class? Thanks

Daniel Pryden
  • 59,486
  • 16
  • 97
  • 135
JEE_program
  • 81
  • 1
  • 7
  • What do you want to have happen if the JSON data you get has types different than what you expect? – Daniel Pryden Jul 01 '18 at 16:15
  • Note also that your code as written should generate a `rawtypes` warning, and you should pay close attention to that warning. The compiler warns you precisely because this code is not type safe. – Daniel Pryden Jul 01 '18 at 16:18
  • Also: If you know the types and structure of the JSON object, then why not use a POJO class to deserialize into? That will probably be more typesafe and fewer lines of code overall. – Daniel Pryden Jul 01 '18 at 16:28
  • I wanted to avoid pojo classes to reduce the code. See my comment below. Thanks – JEE_program Jul 01 '18 at 17:49
  • Adding a class doesn't have to take much code -- for the example data you show, you could declare a class in five lines of code. And if this data represents an entity that you will interact with throughout your program, you should have a dedicated data type for it already. If you're not creating classes to represent the data your program uses, then you're not using the language effectively. – Daniel Pryden Jul 01 '18 at 18:18
  • Thanks Daniel good suggestion if there is no other better solution then I may write pojo classes (getters setters) then – JEE_program Jul 01 '18 at 19:02
  • If you're only using the classes as plain data holders, especially if you keep the class private, then getters and setters are unnecessary. If you have enough validation logic (or might have in the future) to warrant getters and setters, then you *need* to be writing classes for these types rather than passing around raw maps. – Daniel Pryden Jul 01 '18 at 19:11
  • I will be writing public pojo classes. I am not entirely sure about raw maps which I can read about them later – JEE_program Jul 01 '18 at 19:18
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/174127/discussion-between-jee-program-and-daniel-pryden). – JEE_program Jul 01 '18 at 19:45
  • Another related question posted on https://stackoverflow.com/questions/51126337/datetime-fields-in-json?noredirect=1#comment89237837_51126337 – JEE_program Jul 01 '18 at 19:49

3 Answers3

0

Yes, simply call (The mapper is from com.fasterxml.jackson.databind.ObjectMapper)

String age = (String) map.get("age"); 
Infamous
  • 744
  • 11
  • 25
  • You will need an explicit cast to `(String)`. And that cast could fail (with a `ClassCastException`) at runtime if the types don't match. – Daniel Pryden Jul 01 '18 at 16:14
  • Yeah.. you are correct, and it's always good practice to do so to avoid any CCE – Infamous Jul 01 '18 at 16:16
  • 1
    I'm not sure I understand your comment, but the cast certainly does not *avoid* the exception. – Daniel Pryden Jul 01 '18 at 16:20
  • hmm.. care to let me know how to handle an unchecked runtime exception like CCE ? there is no way to know in compile-time whether there is gonna be an exception and the other option is to throw in try-catch blocks at all possible places. What is the better way to do this. – Infamous Jul 01 '18 at 16:29
0

You can do this. But you need to typecast from Object to valid type.

    String jsonString = "{\"age\":12,\"location\":\"UK\",\"gender\":\"male\"}";

    ObjectMapper mapper = new ObjectMapper();
    try {
        Map map = mapper.readValue(jsonString, Map.class);
        System.out.println(map);
        Integer age = (int)map.get("age");
        System.out.println(age);
        System.out.println(map.get("age").getClass());
    }catch (IOException|NullPointerException e){}

You can get the type by using getClass() method.

System.out.println(map.get("age").getClass());
Gaurav Srivastav
  • 2,381
  • 1
  • 15
  • 18
  • Catching (and swallowing!) `Exception` is very bad practice. Either you should show a correct example of how to handle the exceptional case, or your code shouldn't try to catch the exception at all. – Daniel Pryden Jul 01 '18 at 16:16
  • throws IOException, JsonParseException, JsonMappingException either it need to handle and need to add throws in method signature.Its you upto you. – Gaurav Srivastav Jul 01 '18 at 16:22
  • Regarding your edit: technically `getClass()` will return the `Class` object that corresponds to the erasure of the runtime type of the object, not the exact type (although in practice those are often the same thing). In particular, for nested container types like `Map` or `List`, `getClass()` won't tell you anything useful about the element types. – Daniel Pryden Jul 01 '18 at 16:23
  • How you going to do this? – Gaurav Srivastav Jul 01 '18 at 16:24
  • Also: casting through `(int)` rather than `(Integer)` (and then back to `Integer` implicitly!) will invoke unnecessary unboxing and boxing conversions, which will cause a `NullPointerException` to be thrown if the value is actually `null`. If that's what you actually want, then you should probably use something like `Objects.requireNonNull()` for clarity. – Daniel Pryden Jul 01 '18 at 16:25
  • Agree with that, but what about type, how you get the type? – Gaurav Srivastav Jul 01 '18 at 16:26
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/174120/discussion-between-gaurav-srivastav-and-daniel-pryden). – Gaurav Srivastav Jul 01 '18 at 16:28
  • Thanks everyone for your suggestions. I wanted to use Map class to avoid creating Pojo classes for everyone json. But using pojo class I can check the type using Field and getClass(). I tried to use TypeReference class and other ways to get the field types but still no luck. – JEE_program Jul 01 '18 at 17:46
  • I want code to check the types of the field values in the Map to reduce code by avoiding creating many pojo classes I need for many jsons my program should handle – JEE_program Jul 01 '18 at 18:00
0

JSON is an untyped language. So you really have two scenarios here:

Situation #1: You know what types these fields should have, and you want to interact with the values in a safe manner.

In this case, you either need to hard-code the casts to the types you know should exist, or you should create a POJO class to deserialize into. (If this class is only used within a small scope, you could use a private class or even a local class inside the function that uses it.) In any case, either mapper.readValue() or your code will throw an exception if the data doesn't match. You will need to either catch this exception or propagate to your caller. Exactly what kinds of failures are meaningful here really depends on your application, but there are numerous possible failures.

Situation #2: You have no idea what types these fields should have, and you want to discover the types of values to interact with.

Unfortunately, there is no statically-verifiable way to interact with types that will be unknown until runtime. (That's basically the definition of the distinction between static and dynamic type systems.) In practice, with data deserialized from JSON, everything will either be a Number, String, List<?>, Map<?, ?>, or null. So you will need to deal with those cases, but even within a List, you don't have any way to know what type each element is without looping through and examining each one -- and it's possible that the only Java type that you will be able to use to represent the list is List<?> (that is, there may be no valid type that the wildcard can resolve to, e.g. in the case of a heterogeneous list).

That said, it's relatively uncommon to need to analyze a data structure that is truly dynamic. Usually any JSON data does have a standardized structure to it, and so the right solution is to build a data object (as a Java POJO class) that represents that structure, and then transform your incoming data into your object. If the data can't be represented in your object, then that means that the incoming data isn't something your program is prepared to handle. In most cases you should immediately abort with an error rather than trying to muddle on with data that you don't understand (this is the "fail fast" principle).

Daniel Pryden
  • 59,486
  • 16
  • 97
  • 135