Search code examples
javajacksonjackson-databind

How deserialize plain String to Json using Jackson in Java?


I have a simple class as property of mage:

// getter/setter omitted for brevity
public class Magic() {
  String Spell;
  int strength;
}

public class Mage() {
  String name;
  Magic magic;
}

I need to deserialize JSON from 2 different source strings:

{
  "name" : "Sauron",
  "magic" : {
        "spell" : "Tamador",
        "strenght" : 10
   }
}

and

{
  "name" : "Gandalf",
  "magic" : "You shall not pass"
}

or even "You shall not pass" -> Magic object

I thought going with @JsonDeserialize(using = MagicDeserializer.class) would be the way to go with Jackson, but the Parser barfs with "Unrecognized token". Is there a way I can intercept the loading to do my own parsing?


Solution

  • The idea of a custom deserializer is correct, you can extends the StdDeserializer class and in its deserialize method convert the json to a JsonNode separating the two Stringand Object distinct values associated to the magic key in the json:

    public class MagicDeserializer extends StdDeserializer<Magic> {
    
        public MagicDeserializer() {
            super(Magic.class);
        }
    
        @Override
        public Magic deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JsonProcessingException {
            final ObjectCodec codec = jp.getCodec();
            JsonNode root = codec.readTree(jp);
            Magic magic = new Magic();
            if (root.isTextual()) { //<- magic is a string
                magic.setSpell(root.textValue());
                return magic;
            }
            //ok, so magic is an Magic object
            return codec.treeToValue(root, Magic.class);
        }
    }
    

    Then if you annotate your Magic field you can deserialize both the jsons:

    @Data
    public class Mage {
    
        private String name;
        @JsonDeserialize(using = MagicDeserializer.class)
        private Magic magic;
    }
    
    @Data
    public class Magic {
    
        private String Spell;
        private int strength;
    }
    
    Mage sauron = mapper.readValue(json1, Mage.class);
    System.out.println(mapper.writeValueAsString(sauron));
    Mage gandalf = mapper.readValue(json2, Mage.class);
    System.out.println(mapper.writeValueAsString(gandalf));