Search code examples
javajsonjacksongsondeserialization

How to convert below JSON into POJO in java for deserialisation purpose


How to write the equivalent POJO for below type JSON :

[
  {
    "oneImageBlock": [{
      "imagePath": "/abc",
      "mobileImageOverride": {
        "imagePath": "/abc"
      },
      "imageAltText": "image",
      "imageCaptionText": "caption",
      "captionTextReplacement": {
        "type": "overlay"
      },
      "urlPath": "/url",
      "displayURLInModal": true
    }]
  },
  {
    "fourImageBlock": [{
      "imagePath": "/abc",
      "mobileImageOverride": {
        "imagePath": "/abc"
      },
      "imageAltText": "image",
      "imageCaptionText": "caption",
      "captionTextReplacement": {
        "type": "overlay/Below Image"
      },
      "urlPath": "/l/123",
      "displayURLInModal": true
     },
     {
      "imagePath": "/abc",
      "mobileImageOverride": {
        "imagePath": "/abc"
      },
      "imageAltText": "image",
      "imageCaptionText": "caption",
      "captionTextReplacement": {
        "type": "overlay/Below Image"
      },
      "urlPath": "/l/123",
      "displayURLInModal": true
     },
     {
      "imagePath": "/abc",
      "mobileImageOverride": {
        "imagePath": "/abc"
      },
      "imageAltText": "image",
      "imageCaptionText": "caption",
      "captionTextReplacement": {
        "type": "overlay/Below Image"
      },
      "urlPath": "/l/123",
      "displayURLInModal": true
     },
     {
      "imagePath": "/abc",
      "mobileImageOverride": {
        "imagePath": "/abc"
      },
      "imageAltText": "image",
      "imageCaptionText": "caption",
      "captionTextReplacement": {
        "type": "overlay/Below Image"
      },
      "urlPath": "/l/123",
      "displayURLInModal": true
     } 
    ]
  },
  {
    "fourImageBlock": [
      
    ]
  },
  {
    "oneImageBlock": [
      
    ]
  },
  {
    "oneTextBlock": [
      "text": "test",
      "textAlignment": "centre",
      "mobileTextOverride": {
        "text": "test"
      },
      "altText": "alt",
      "urlPath": "/url"
    ]
  },
  {
    "fourImageBlock": [
      
    ]
  }
]

provided there can be multiple objects with the same key in the JSON like here in example it has 2 occurrence of oneImageBlock & 3 occurrence of fourImageBlock and also there is no fixed order as well i.e it's possible that in the JSON we have first 5 objects with fourImageBlock followed by 2 objects with oneImageBlock again followed by 2 objects with fourImageBlock.


Solution

  • The easiest way to deserialize your data would be to set up some POJOs that conform to your JSON data:

    Since your top-level objects can contain the keys: oneImageBlock, fourImageBlock, or oneTextBlock, you need an object that defines all three of those as possibilities. They are defined in the Entry class below.

    import java.io.File;
    import java.io.IOException;
    import com.fasterxml.jackson.core.exc.StreamReadException;
    import com.fasterxml.jackson.databind.DatabindException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    public class BlockDeserializer {
        protected static ObjectMapper mapper = new ObjectMapper();
    
        public static void main(String[] args) throws StreamReadException, DatabindException, IOException {
            Entry[] entries = mapper.readValue(new File("src/main/resources/blocks.json"), Entry[].class);
            System.out.println(entries);
        }
    
        private static class Entry {
            public ImageBlock[] oneImageBlock;
            public ImageBlock[] fourImageBlock;
            public TextBlock[] oneTextBlock;
            public TextBlock[] fourTextBlock;
        }
    
        private static class ImageBlock {
            public String imagePath;
            public MobileImageOverride mobileImageOverride;
            public String imageAltText;
            public String imageCaptionText;
            public CaptionTextReplacement captionTextReplacement;
            public String urlPath;
            public boolean displayURLInModal;
        }
    
        private static class TextBlock {
            public String text;
            public String textAlignment;
            public MobileTextOverride mobileTextOverride;
            public String altText;
            public String urlPath;
        }
    
        private static class CaptionTextReplacement {
            public String type;
        }
    
        private static class MobileImageOverride {
            public String imagePath;
        }
    
        private static class MobileTextOverride {
            public String text;
        }
    }
    

    Also, make sure your oneTextBlock value is an array of objects. Looks like your JSON is invalid.

    [
      //...
      {
        "oneTextBlock": [
          {
            "text": "test",
            "textAlignment": "centre",
            "mobileTextOverride": {
              "text": "test"
            },
            "altText": "alt",
            "urlPath": "/url"
          }
        ]
      },
      // ...
    ]
    

    Alternatively, you can create your own deserializer so that you can correctly deserialize into the correct "block" type.

    I removed the Entry concept and added a Block interface to all the sub-blocks.

    import java.io.File;
    import java.io.IOException;
    
    import com.fasterxml.jackson.core.JacksonException;
    import com.fasterxml.jackson.core.JsonParser;
    import com.fasterxml.jackson.core.exc.StreamReadException;
    import com.fasterxml.jackson.databind.DatabindException;
    import com.fasterxml.jackson.databind.DeserializationContext;
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
    import com.fasterxml.jackson.databind.module.SimpleModule;
    
    public class BlockDeserializerDriver {
        protected static ObjectMapper mapper;
        protected static SimpleModule module;
    
        static {
            mapper = new ObjectMapper();
            module = new SimpleModule();
    
            module.addDeserializer(Block.class, new BlockDeserializer());
            mapper.registerModule(module);
        }
    
        public static void main(String[] args) throws StreamReadException, DatabindException, IOException {
            Block[] entries = loadBlocks("src/main/resources/blocks.json");
    
            System.out.println(entries[0] instanceof OneImageBlock); // true
            System.out.println(entries[1] instanceof FourImageBlock); // true
            System.out.println(entries[2] instanceof FourImageBlock); // true
            System.out.println(entries[3] instanceof OneImageBlock); // true
            System.out.println(entries[4] instanceof OneTextBlock); // true
            System.out.println(entries[5] instanceof FourImageBlock); // true
        }
    
        public static Block[] loadBlocks(String filePath) throws StreamReadException, DatabindException, IOException {
            return mapper.readValue(new File(filePath), Block[].class);
        }
    
        private static interface Block {
        }
    
        private static class OneImageBlock implements Block {
            public ImageBlock[] oneImageBlock;
        }
    
        private static class FourImageBlock implements Block {
            public ImageBlock[] fourImageBlock;
        }
    
        private static class OneTextBlock implements Block {
            public TextBlock[] oneTextBlock;
        }
    
        private static class FourTextBlock implements Block {
            public TextBlock[] fourTextBlock;
        }
    
        private static class ImageBlock {
            public String imagePath;
            public MobileImageOverride mobileImageOverride;
            public String imageAltText;
            public String imageCaptionText;
            public CaptionTextReplacement captionTextReplacement;
            public String urlPath;
            public boolean displayURLInModal;
        }
    
        private static class TextBlock {
            public String text;
            public String textAlignment;
            public MobileTextOverride mobileTextOverride;
            public String altText;
            public String urlPath;
        }
    
        private static class CaptionTextReplacement {
            public String type;
        }
    
        private static class MobileImageOverride {
            public String imagePath;
        }
    
        private static class MobileTextOverride {
            public String text;
        }
    
        // See: https://www.baeldung.com/jackson-deserialization
        private static class BlockDeserializer extends StdDeserializer<Block> {
            public BlockDeserializer() {
                this(null);
            }
    
            public BlockDeserializer(Class<?> vc) {
                super(vc);
            }
    
            @Override
            public Block deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JacksonException {
                JsonNode node = jp.getCodec().readTree(jp);
    
                if (node.has("oneImageBlock")) {
                    return mapper.convertValue(node, OneImageBlock.class);
                } else if (node.has("fourImageBlock")) {
                    return mapper.convertValue(node, FourImageBlock.class);
                } else if (node.has("oneTextBlock")) {
                    return mapper.convertValue(node, OneTextBlock.class);
                } else if (node.has("fourTextBlock")) {
                    return mapper.convertValue(node, FourTextBlock.class);
                }
    
                return null; // Could not parse Block
            }
        }
    }