Polymorphic Type Handling

জ্যাকসন (Jackson) - Java Technologies

272

Jackson-এ Polymorphic Type Handling ব্যবহৃত হয় যখন কোনো ক্লাসের অবজেক্ট সিরিয়ালাইজ বা ডি-সিরিয়ালাইজ করতে হয় এবং সেই ক্লাসে ইনহেরিটেন্স বা পলিমরফিজম থাকে। এটি বিশেষভাবে উপকারী যখন আপনি একটি বেস ক্লাস এবং একাধিক সাব-ক্লাস নিয়ে কাজ করেন।


কেন Polymorphic Type Handling প্রয়োজন?

যখন আপনি বেস ক্লাসের টাইপ দিয়ে সাব-ক্লাসের অবজেক্ট সিরিয়ালাইজ বা ডি-সিরিয়ালাইজ করেন, তখন Jackson জানে না কোন সাব-ক্লাসটি ব্যবহার করা হবে। এই সমস্যার সমাধানে Polymorphic Type Handling ব্যবহার করা হয়।


Jackson-এ Polymorphic Type Handling কনফিগার করার উপায়

  1. @JsonTypeInfo: টাইপ ইনফরমেশন যোগ করতে ব্যবহৃত হয়।
  2. @JsonSubTypes: কোন সাব-ক্লাসগুলো ব্যবহৃত হবে তা নির্ধারণ করতে ব্যবহৃত হয়।

উদাহরণ: Polymorphic Type Handling

বেস ক্লাস এবং সাব-ক্লাস তৈরি করুন

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

// Base class
@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property = "type" // Field to determine the type
)
@JsonSubTypes({
        @JsonSubTypes.Type(value = Car.class, name = "car"),
        @JsonSubTypes.Type(value = Truck.class, name = "truck")
})
abstract class Vehicle {
    private String brand;

    public Vehicle() {}

    public Vehicle(String brand) {
        this.brand = brand;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }
}

// Subclass 1
class Car extends Vehicle {
    private int seatingCapacity;

    public Car() {}

    public Car(String brand, int seatingCapacity) {
        super(brand);
        this.seatingCapacity = seatingCapacity;
    }

    public int getSeatingCapacity() {
        return seatingCapacity;
    }

    public void setSeatingCapacity(int seatingCapacity) {
        this.seatingCapacity = seatingCapacity;
    }
}

// Subclass 2
class Truck extends Vehicle {
    private int loadCapacity;

    public Truck() {}

    public Truck(String brand, int loadCapacity) {
        super(brand);
        this.loadCapacity = loadCapacity;
    }

    public int getLoadCapacity() {
        return loadCapacity;
    }

    public void setLoadCapacity(int loadCapacity) {
        this.loadCapacity = loadCapacity;
    }
}

ObjectMapper দিয়ে সিরিয়ালাইজ এবং ডি-সিরিয়ালাইজ করুন

import com.fasterxml.jackson.databind.ObjectMapper;

public class PolymorphicExample {
    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();

        // Serialize: Car object
        Vehicle car = new Car("Toyota", 5);
        String carJson = mapper.writeValueAsString(car);
        System.out.println("Serialized Car: " + carJson);

        // Serialize: Truck object
        Vehicle truck = new Truck("Volvo", 10000);
        String truckJson = mapper.writeValueAsString(truck);
        System.out.println("Serialized Truck: " + truckJson);

        // Deserialize: Car JSON
        String carJsonInput = "{\"type\":\"car\",\"brand\":\"Toyota\",\"seatingCapacity\":5}";
        Vehicle deserializedCar = mapper.readValue(carJsonInput, Vehicle.class);
        System.out.println("Deserialized Car: " + deserializedCar.getClass().getSimpleName());

        // Deserialize: Truck JSON
        String truckJsonInput = "{\"type\":\"truck\",\"brand\":\"Volvo\",\"loadCapacity\":10000}";
        Vehicle deserializedTruck = mapper.readValue(truckJsonInput, Vehicle.class);
        System.out.println("Deserialized Truck: " + deserializedTruck.getClass().getSimpleName());
    }
}

Output

Serialized Car: {"type":"car","brand":"Toyota","seatingCapacity":5}
Serialized Truck: {"type":"truck","brand":"Volvo","loadCapacity":10000}
Deserialized Car: Car
Deserialized Truck: Truck

নির্দিষ্ট কনফিগারেশন

  1. @JsonTypeInfo.Property: এটি নির্ধারণ করে কোন প্রোপার্টি টাইপ ইনফরমেশন বহন করবে (যেমন type)।
  2. @JsonSubTypes: সব সাব-ক্লাস এবং তাদের নাম নির্ধারণ করে।
  3. Default Typing: যদি আপনি সব ক্লাসের জন্য ডিফল্ট টাইপ ইনফরমেশন যোগ করতে চান, তবে ObjectMapper.enableDefaultTyping() ব্যবহার করতে পারেন।

উন্নত ব্যবহার: DefaultTyping

ডিফল্ট টাইপিং ব্যবহার করে আপনি ম্যানুয়ালি @JsonTypeInfo এবং @JsonSubTypes ব্যবহার না করেও টাইপ ইনফরমেশন যোগ করতে পারেন।

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;

public class DefaultTypingExample {
    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(), DefaultTyping.OBJECT_AND_NON_CONCRETE);

        Vehicle car = new Car("Honda", 4);
        String json = mapper.writeValueAsString(car);
        System.out.println("DefaultTyping Serialized JSON: " + json);

        Vehicle deserializedCar = mapper.readValue(json, Vehicle.class);
        System.out.println("Deserialized Vehicle: " + deserializedCar.getClass().getSimpleName());
    }
}

Jackson-এ Polymorphic Type Handling একটি অত্যন্ত কার্যকর ফিচার যা আপনাকে পলিমরফিক অবজেক্টের সিরিয়ালাইজ এবং ডি-সিরিয়ালাইজ করার ক্ষমতা দেয়। এটি বড় প্রজেক্টে ইনহেরিটেন্স হ্যান্ডলিং এবং ডেটা ট্রান্সফার সহজ করে।

Content added By

Polymorphic Serialization এবং Deserialization হল এমন একটি পদ্ধতি, যেখানে একটি প্যারেন্ট ক্লাস বা ইন্টারফেসের ইনস্ট্যান্সকে JSON-এ রূপান্তর করা হয় এবং JSON থেকে পুনরায় সঠিক সাবক্লাস বা ইমপ্লিমেন্টেশন ক্লাস হিসেবে রূপান্তর করা হয়।

Jackson-এ এই কাজটি করতে আমরা সাধারণত @JsonTypeInfo এবং @JsonSubTypes এনোটেশন ব্যবহার করি।


কেন Polymorphic Serialization দরকার?

যখন আপনার একটি ক্লাস হায়ারার্কি থাকে (যেমন: একটি প্যারেন্ট ক্লাস এবং তার একাধিক চাইল্ড ক্লাস) এবং আপনি JSON ফাইল থেকে সঠিক সাবক্লাস ইনস্ট্যান্স পুনরায় তৈরি করতে চান।


ধাপ ১: ক্লাস হায়ারার্কি তৈরি করা

প্যারেন্ট ক্লাস এবং চাইল্ড ক্লাসগুলো তৈরি:

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

// প্যারেন্ট ক্লাস
@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property = "type" // এই প্রোপার্টি সাবক্লাস চিহ্নিত করবে
)
@JsonSubTypes({
        @JsonSubTypes.Type(value = Car.class, name = "car"),
        @JsonSubTypes.Type(value = Truck.class, name = "truck")
})
abstract class Vehicle {
    private String brand;

    // Constructor, Getters, and Setters
    public Vehicle(String brand) {
        this.brand = brand;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }
}

// চাইল্ড ক্লাস ১
class Car extends Vehicle {
    private int seatingCapacity;

    public Car(String brand, int seatingCapacity) {
        super(brand);
        this.seatingCapacity = seatingCapacity;
    }

    public int getSeatingCapacity() {
        return seatingCapacity;
    }

    public void setSeatingCapacity(int seatingCapacity) {
        this.seatingCapacity = seatingCapacity;
    }
}

// চাইল্ড ক্লাস ২
class Truck extends Vehicle {
    private int loadCapacity;

    public Truck(String brand, int loadCapacity) {
        super(brand);
        this.loadCapacity = loadCapacity;
    }

    public int getLoadCapacity() {
        return loadCapacity;
    }

    public void setLoadCapacity(int loadCapacity) {
        this.loadCapacity = loadCapacity;
    }
}

ধাপ ২: ObjectMapper দিয়ে Serialization এবং Deserialization

Serialization (Object থেকে JSON):

import com.fasterxml.jackson.databind.ObjectMapper;

public class PolymorphicSerialization {
    public static void main(String[] args) {
        try {
            ObjectMapper objectMapper = new ObjectMapper();

            // একটি Car Object তৈরি
            Vehicle car = new Car("Toyota", 5);

            // Car Object কে JSON-এ রূপান্তর
            String carJson = objectMapper.writeValueAsString(car);
            System.out.println("Car JSON: " + carJson);

            // একটি Truck Object তৈরি
            Vehicle truck = new Truck("Volvo", 10000);

            // Truck Object কে JSON-এ রূপান্তর
            String truckJson = objectMapper.writeValueAsString(truck);
            System.out.println("Truck JSON: " + truckJson);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

আউটপুট:

Car JSON: {"type":"car","brand":"Toyota","seatingCapacity":5}
Truck JSON: {"type":"truck","brand":"Volvo","loadCapacity":10000}

Deserialization (JSON থেকে Object):

import com.fasterxml.jackson.databind.ObjectMapper;

public class PolymorphicDeserialization {
    public static void main(String[] args) {
        try {
            ObjectMapper objectMapper = new ObjectMapper();

            // Car JSON
            String carJson = "{\"type\":\"car\",\"brand\":\"Toyota\",\"seatingCapacity\":5}";

            // JSON থেকে Object
            Vehicle car = objectMapper.readValue(carJson, Vehicle.class);
            System.out.println("Deserialized Car: " + car.getClass().getSimpleName() + " - Brand: " + car.getBrand());

            // Truck JSON
            String truckJson = "{\"type\":\"truck\",\"brand\":\"Volvo\",\"loadCapacity\":10000}";

            // JSON থেকে Object
            Vehicle truck = objectMapper.readValue(truckJson, Vehicle.class);
            System.out.println("Deserialized Truck: " + truck.getClass().getSimpleName() + " - Brand: " + truck.getBrand());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

আউটপুট:

Deserialized Car: Car - Brand: Toyota
Deserialized Truck: Truck - Brand: Volvo

গুরুত্বপূর্ণ পয়েন্ট

  1. @JsonTypeInfo:
    • use: সাবক্লাস চিহ্নিত করতে কী ব্যবহার হবে। যেমন: Id.NAME ব্যবহার করলে নাম দিয়ে চিহ্নিত হবে।
    • include: টাইপ ইনফরমেশন কোথায় থাকবে। সাধারণত As.PROPERTY ব্যবহার করা হয়।
    • property: JSON ফিল্ডের নাম, যা টাইপ ইনফরমেশন রাখবে।
  2. @JsonSubTypes:
    • সাবক্লাসগুলোর তালিকা এবং তাদের টাইপ নাম উল্লেখ করতে ব্যবহৃত হয়।
  3. Serialization এবং Deserialization:
    • Object থেকে JSON করার সময় পলিমরফিক টাইপ ইনফরমেশন স্বয়ংক্রিয়ভাবে যোগ হয়।
    • JSON থেকে Object করার সময় টাইপ ইনফরমেশন ব্যবহার করে সঠিক ক্লাসে রূপান্তর হয়।
  4. Type Inclusion Options:
    • As.PROPERTY: JSON অবজেক্টের একটি ফিল্ড হিসেবে ইনফরমেশন থাকবে।
    • As.WRAPPER_OBJECT: JSON অবজেক্ট পুরো টাইপ ইনফরমেশন দিয়ে মোড়ানো থাকে।

উন্নত ব্যবহার: Global Configuration

ObjectMapper-এর মাধ্যমে গ্লোবাল কনফিগারেশন দিয়ে এই টাইপ ইনফরমেশন যুক্ত করা যায়:

ObjectMapper mapper = new ObjectMapper();
mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(),
        ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);

  • Jackson-এ Polymorphic Serialization এবং Deserialization জটিল ক্লাস হায়ারার্কি সহজে হ্যান্ডেল করতে সহায়তা করে।
  • @JsonTypeInfo এবং @JsonSubTypes ব্যবহার করে সাবক্লাসগুলোকে সহজে চিহ্নিত এবং JSON-এ রূপান্তর করা যায়।
  • বড় প্রজেক্টে এই কৌশলটি অত্যন্ত কার্যকর।
Content added By

Jackson-এর @JsonTypeInfo এবং @JsonSubTypes অ্যানোটেশন ব্যবহার করে আপনি JSON ডেটা সিরিয়ালাইজ এবং ডেসিরিয়ালাইজ করার সময় পলিমরফিক টাইপ হ্যান্ডল করতে পারেন। এটি বিশেষভাবে দরকার হয় যখন আপনার একটি প্যারেন্ট ক্লাস এবং একাধিক সাবক্লাস থাকে এবং JSON ডেটা থেকে সঠিক সাবক্লাসে ডেসিরিয়ালাইজ করার প্রয়োজন হয়।


@JsonTypeInfo

@JsonTypeInfo প্যারেন্ট ক্লাসে ব্যবহৃত হয় এবং JSON ডেটার মধ্যে টাইপ সম্পর্কিত তথ্য সংরক্ষণ করে। এটি পলিমরফিজম হ্যান্ডল করার মূল অংশ।

প্যারামিটার

  • use: কীভাবে টাইপ সম্পর্কিত তথ্য উল্লেখ করা হবে। উদাহরণ: JsonTypeInfo.Id.CLASS বা JsonTypeInfo.Id.NAME.
  • include: টাইপ সম্পর্কিত তথ্য JSON-এ কোথায় অন্তর্ভুক্ত হবে। উদাহরণ: JsonTypeInfo.As.PROPERTY.
  • property: JSON ডেটায় টাইপের জন্য ব্যবহৃত প্রপার্টি নাম। উদাহরণ: "type".
  • defaultImpl: কোনো টাইপ না পাওয়া গেলে ডিফল্ট ক্লাস ব্যবহার।

@JsonSubTypes

@JsonSubTypes ব্যবহার করে প্যারেন্ট ক্লাসের বিভিন্ন সাবক্লাসের তথ্য প্রদান করা হয়। এতে টাইপ এবং সংশ্লিষ্ট ক্লাস ম্যাপিং করা হয়।

প্যারামিটার

  • value: একাধিক @JsonSubTypes.Type যা টাইপ এবং ক্লাসের মধ্যে সম্পর্ক সংজ্ঞায়িত করে।

কোড উদাহরণ

ধরুন, আমাদের একটি প্যারেন্ট ক্লাস Animal এবং দুইটি সাবক্লাস DogCat রয়েছে।

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

// Parent class
@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property = "type"
)
@JsonSubTypes({
        @JsonSubTypes.Type(value = Dog.class, name = "dog"),
        @JsonSubTypes.Type(value = Cat.class, name = "cat")
})
abstract class Animal {
    private String name;

    // Constructors, Getters, and Setters
    public Animal() {}

    public Animal(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

// Subclass 1
class Dog extends Animal {
    private String breed;

    public Dog() {}

    public Dog(String name, String breed) {
        super(name);
        this.breed = breed;
    }

    public String getBreed() {
        return breed;
    }

    public void setBreed(String breed) {
        this.breed = breed;
    }

    @Override
    public String toString() {
        return "Dog{name='" + getName() + "', breed='" + breed + "'}";
    }
}

// Subclass 2
class Cat extends Animal {
    private boolean indoor;

    public Cat() {}

    public Cat(String name, boolean indoor) {
        super(name);
        this.indoor = indoor;
    }

    public boolean isIndoor() {
        return indoor;
    }

    public void setIndoor(boolean indoor) {
        this.indoor = indoor;
    }

    @Override
    public String toString() {
        return "Cat{name='" + getName() + "', indoor=" + indoor + "}";
    }
}

Serialization এবং Deserialization

JSON ডেটা সিরিয়ালাইজ এবং ডেসিরিয়ালাইজ করার জন্য ObjectMapper ব্যবহার করা হবে।

import com.fasterxml.jackson.databind.ObjectMapper;

public class JsonTypeInfoExample {
    public static void main(String[] args) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();

        // Serialize
        Animal dog = new Dog("Buddy", "Golden Retriever");
        Animal cat = new Cat("Whiskers", true);

        String dogJson = objectMapper.writeValueAsString(dog);
        String catJson = objectMapper.writeValueAsString(cat);

        System.out.println("Serialized Dog JSON: " + dogJson);
        System.out.println("Serialized Cat JSON: " + catJson);

        // Deserialize
        Animal deserializedDog = objectMapper.readValue(dogJson, Animal.class);
        Animal deserializedCat = objectMapper.readValue(catJson, Animal.class);

        System.out.println("Deserialized Dog Object: " + deserializedDog);
        System.out.println("Deserialized Cat Object: " + deserializedCat);
    }
}

Output

Serialized JSON

Serialized Dog JSON: {"type":"dog","name":"Buddy","breed":"Golden Retriever"}
Serialized Cat JSON: {"type":"cat","name":"Whiskers","indoor":true}

Deserialized Objects

Deserialized Dog Object: Dog{name='Buddy', breed='Golden Retriever'}
Deserialized Cat Object: Cat{name='Whiskers', indoor=true}

কীভাবে কাজ করে?

  1. Serialization: @JsonTypeInfo অ্যানোটেশনের মাধ্যমে type ফিল্ডটি JSON-এ যোগ করা হয়, যা টাইপ নির্দেশ করে।
  2. Deserialization: @JsonSubTypes এর মাধ্যমে টাইপ এবং সাবক্লাসের মধ্যে সম্পর্ক সংজ্ঞায়িত করা হয়। টাইপ ফিল্ডের ভিত্তিতে সঠিক সাবক্লাস তৈরি হয়।

ব্যবহার ক্ষেত্র

  1. Polymorphism Handling: একাধিক সাবক্লাসের সাথে JSON ডেটা হ্যান্ডলিং।
  2. Dynamic Type Mapping: JSON-এ টাইপ ফিল্ডের উপর ভিত্তি করে সঠিক সাবক্লাস নির্বাচন।
  3. REST API: API এর মাধ্যমে পলিমরফিক ডেটা আদান-প্রদান।

@JsonTypeInfo এবং @JsonSubTypes ব্যবহার করে পলিমরফিক JSON ডেটার প্রক্রিয়াকরণ সহজ এবং কার্যকর করা যায়। এটি বিশেষভাবে দরকার হয় যখন একাধিক সাবক্লাস এবং পলিমরফিক আচরণ পরিচালনা করতে হয়।

Content added By

Jackson ডিফল্টভাবে abstract classes বা interfaces সিরিয়ালাইজ এবং ডেসিরিয়ালাইজ করতে পারে না, কারণ এগুলোর ইনস্ট্যান্স তৈরি করা সম্ভব নয়। তবে, সঠিক কনফিগারেশন এবং কাস্টম পলিসি ব্যবহারের মাধ্যমে Jackson এ abstract classes এবং interfaces হ্যান্ডল করা সম্ভব।


Abstract Class এবং Interface কেন সমস্যা তৈরি করে?

  1. Abstract Class: এটি সরাসরি ইনস্ট্যান্সিয়েট করা যায় না।
  2. Interface: এটি শুধুমাত্র মেথডের সিগনেচার সংজ্ঞায়িত করে, ইনস্ট্যান্সিয়েট করা যায় না।

এগুলোর জন্য Jackson কে জানাতে হবে:

  • ডেটার আসল টাইপ (Sub-Class)।
  • কিভাবে সিরিয়ালাইজ এবং ডেসিরিয়ালাইজ করতে হবে।

সমাধানের পদ্ধতি

১. Type Information অন্তর্ভুক্ত করা

Jackson-এর @JsonTypeInfo এবং @JsonSubTypes অ্যানোটেশন ব্যবহার করে টাইপ ইনফরমেশন সংজ্ঞায়িত করা যায়।

উদাহরণ:
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

// Abstract Class
@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,      // টাইপ ইনফরমেশন সংরক্ষণ
    include = JsonTypeInfo.As.PROPERTY,
    property = "type"               // JSON-এ "type" ফিল্ড যোগ হবে
)
@JsonSubTypes({
    @JsonSubTypes.Type(value = Car.class, name = "car"),
    @JsonSubTypes.Type(value = Truck.class, name = "truck")
})
abstract class Vehicle {
    public String brand;
}

// Sub-Class 1
class Car extends Vehicle {
    public int seatingCapacity;

    public Car() {}
}

// Sub-Class 2
class Truck extends Vehicle {
    public double payloadCapacity;

    public Truck() {}
}
Serialization:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

public class SerializationExample {
    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.enable(SerializationFeature.INDENT_OUTPUT);

        Vehicle car = new Car();
        car.brand = "Toyota";
        ((Car) car).seatingCapacity = 5;

        String json = mapper.writeValueAsString(car);
        System.out.println("Serialized JSON:\n" + json);
    }
}
Output:
{
  "type": "car",
  "brand": "Toyota",
  "seatingCapacity": 5
}
Deserialization:
public class DeserializationExample {
    public static void main(String[] args) throws Exception {
        String json = "{ \"type\": \"truck\", \"brand\": \"Ford\", \"payloadCapacity\": 10000.0 }";

        ObjectMapper mapper = new ObjectMapper();
        Vehicle vehicle = mapper.readValue(json, Vehicle.class);

        System.out.println("Deserialized Vehicle: " + vehicle.brand + ", Type: " + vehicle.getClass().getSimpleName());
    }
}
Output:
Deserialized Vehicle: Ford, Type: Truck

২. Mix-in Annotations ব্যবহার করা

যদি আপনার কাছে Abstract Class বা Interface-এর সোর্স কোড না থাকে, তবে Mix-in Annotations ব্যবহার করে টাইপ ইনফরমেশন যোগ করা সম্ভব।

উদাহরণ:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;

// Abstract Class
abstract class Shape {
    public String color;
}

// Sub-Class
class Circle extends Shape {
    public double radius;
}

// Mix-in Annotation
@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.PROPERTY,
    property = "type"
)
@JsonSubTypes({
    @JsonSubTypes.Type(value = Circle.class, name = "circle")
})
abstract class ShapeMixIn {}
কনফিগার করা:
public class MixInExample {
    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.addMixIn(Shape.class, ShapeMixIn.class); // Mix-in যোগ করা

        Shape shape = new Circle();
        shape.color = "Red";
        ((Circle) shape).radius = 5.5;

        String json = mapper.writeValueAsString(shape);
        System.out.println("Serialized JSON:\n" + json);

        Shape deserializedShape = mapper.readValue(json, Shape.class);
        System.out.println("Deserialized Shape Type: " + deserializedShape.getClass().getSimpleName());
    }
}
Output:
{
  "type": "circle",
  "color": "Red",
  "radius": 5.5
}

৩. Custom Serializer এবং Deserializer ব্যবহার করা

Custom Serializer:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

import java.io.IOException;

public class VehicleSerializer extends StdSerializer<Vehicle> {

    public VehicleSerializer() {
        super(Vehicle.class);
    }

    @Override
    public void serialize(Vehicle value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        gen.writeStartObject();
        gen.writeStringField("type", value.getClass().getSimpleName().toLowerCase());
        gen.writeStringField("brand", value.brand);

        if (value instanceof Car) {
            gen.writeNumberField("seatingCapacity", ((Car) value).seatingCapacity);
        } else if (value instanceof Truck) {
            gen.writeNumberField("payloadCapacity", ((Truck) value).payloadCapacity);
        }

        gen.writeEndObject();
    }
}
Custom Deserializer:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;

import java.io.IOException;

public class VehicleDeserializer extends StdDeserializer<Vehicle> {

    public VehicleDeserializer() {
        super(Vehicle.class);
    }

    @Override
    public Vehicle deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        JsonNode node = p.getCodec().readTree(p);
        String type = node.get("type").asText();

        Vehicle vehicle;
        if ("car".equals(type)) {
            vehicle = new Car();
            ((Car) vehicle).seatingCapacity = node.get("seatingCapacity").asInt();
        } else if ("truck".equals(type)) {
            vehicle = new Truck();
            ((Truck) vehicle).payloadCapacity = node.get("payloadCapacity").asDouble();
        } else {
            throw new IllegalArgumentException("Unknown type: " + type);
        }

        vehicle.brand = node.get("brand").asText();
        return vehicle;
    }
}
Module Configuration:
public class CustomSerializerExample {
    public static void main(String[] args) throws Exception {
        SimpleModule module = new SimpleModule();
        module.addSerializer(Vehicle.class, new VehicleSerializer());
        module.addDeserializer(Vehicle.class, new VehicleDeserializer());

        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(module);

        Vehicle car = new Car();
        car.brand = "Honda";
        ((Car) car).seatingCapacity = 4;

        String json = mapper.writeValueAsString(car);
        System.out.println("Custom Serialized JSON:\n" + json);

        Vehicle deserializedVehicle = mapper.readValue(json, Vehicle.class);
        System.out.println("Custom Deserialized Vehicle: " + deserializedVehicle.brand);
    }
}

উপকারিতা

  1. Abstract Class এবং Interface হ্যান্ডল করা সহজতর।
  2. Runtime Polymorphism সাপোর্ট।
  3. Custom Serialization/Deserialization এর মাধ্যমে নির্দিষ্ট নিয়ম প্রয়োগ।

Jackson-এর এই ফিচারগুলো ব্যবহার করে আপনি Abstract Classes এবং Interfaces নিয়ে সহজে কাজ করতে পারবেন।

Content added By

Java-তে Polymorphic Data Structures মানে এমন ডেটা স্ট্রাকচার যা একাধিক টাইপের অবজেক্ট ধারণ করতে পারে। Jackson লাইব্রেরি এ ধরনের ডেটা Serialize এবং Deserialize করার জন্য সহজ পদ্ধতি সরবরাহ করে।

1. Polymorphic Serialization/Deserialization সমস্যা

যখন কোনো প্যারেন্ট ক্লাসের মাধ্যমে চাইল্ড ক্লাসগুলোকে হ্যান্ডল করতে হয়, তখন Serialize বা Deserialize করার সময় ক্লাসের সঠিক টাইপ নির্ধারণ করা জটিল হতে পারে।

Jackson এই সমস্যার সমাধান করে @JsonTypeInfo এবং @JsonSubTypes অ্যানোটেশন ব্যবহার করে।


2. উদাহরণ: Polymorphic Data Structure

ধরা যাক, আমাদের কাছে একটি Shape ইন্টারফেস বা প্যারেন্ট ক্লাস রয়েছে এবং তার দুইটি চাইল্ড ক্লাস Circle এবং Rectangle

প্যারেন্ট ও চাইল্ড ক্লাস তৈরি

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

// প্যারেন্ট ক্লাস বা ইন্টারফেস
@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,      // টাইপের আইডেন্টিফায়ার হিসেবে টাইপের নাম ব্যবহার হবে
        include = JsonTypeInfo.As.PROPERTY,  // টাইপের তথ্য JSON-এর একটি প্রপার্টি হিসেবে যোগ হবে
        property = "type"                // টাইপের প্রপার্টির নাম
)
@JsonSubTypes({
        @JsonSubTypes.Type(value = Circle.class, name = "circle"),
        @JsonSubTypes.Type(value = Rectangle.class, name = "rectangle")
})
abstract class Shape {
    private String color;

    // Constructors, Getters, and Setters
    public Shape() {}

    public Shape(String color) {
        this.color = color;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return "Shape{color='" + color + "'}";
    }
}

// চাইল্ড ক্লাস ১: Circle
class Circle extends Shape {
    private double radius;

    public Circle() {}

    public Circle(String color, double radius) {
        super(color);
        this.radius = radius;
    }

    public double getRadius() {
        return radius;
    }

    public void setRadius(double radius) {
        this.radius = radius;
    }

    @Override
    public String toString() {
        return "Circle{" + "color='" + getColor() + '\'' + ", radius=" + radius + '}';
    }
}

// চাইল্ড ক্লাস ২: Rectangle
class Rectangle extends Shape {
    private double length;
    private double width;

    public Rectangle() {}

    public Rectangle(String color, double length, double width) {
        super(color);
        this.length = length;
        this.width = width;
    }

    public double getLength() {
        return length;
    }

    public void setLength(double length) {
        this.length = length;
    }

    public double getWidth() {
        return width;
    }

    public void setWidth(double width) {
        this.width = width;
    }

    @Override
    public String toString() {
        return "Rectangle{" + "color='" + getColor() + '\'' + ", length=" + length + ", width=" + width + '}';
    }
}

3. Serialization এবং Deserialization

Serialization: Polymorphic Object থেকে JSON

import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.ArrayList;
import java.util.List;

public class PolymorphicSerializationExample {
    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();

        // Polymorphic objects তৈরি
        List<Shape> shapes = new ArrayList<>();
        shapes.add(new Circle("Red", 5.5));
        shapes.add(new Rectangle("Blue", 4.0, 6.0));

        // Serialize
        String json = mapper.writeValueAsString(shapes);
        System.out.println("Serialized JSON: " + json);
    }
}

আউটপুট:

[
  {
    "type": "circle",
    "color": "Red",
    "radius": 5.5
  },
  {
    "type": "rectangle",
    "color": "Blue",
    "length": 4.0,
    "width": 6.0
  }
]

Deserialization: JSON থেকে Polymorphic Object

public class PolymorphicDeserializationExample {
    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();

        String json = """
        [
          {
            "type": "circle",
            "color": "Red",
            "radius": 5.5
          },
          {
            "type": "rectangle",
            "color": "Blue",
            "length": 4.0,
            "width": 6.0
          }
        ]
        """;

        // Deserialize
        List<Shape> shapes = mapper.readValue(json, mapper.getTypeFactory().constructCollectionType(List.class, Shape.class));
        for (Shape shape : shapes) {
            System.out.println("Deserialized Shape: " + shape);
        }
    }
}

আউটপুট:

Deserialized Shape: Circle{color='Red', radius=5.5}
Deserialized Shape: Rectangle{color='Blue', length=4.0, width=6.0}

4. টিপস এবং গুরুত্বপূর্ণ বিষয়

  1. @JsonTypeInfo এবং @JsonSubTypes ব্যবহার করুন:
    • @JsonTypeInfo JSON ডকুমেন্টে টাইপ সম্পর্কিত তথ্য যোগ করে।
    • @JsonSubTypes চাইল্ড টাইপগুলোর নাম ও ক্লাস ম্যাপিং করে।
  2. Type Identifier কাস্টমাইজেশন: যদি type প্রপার্টি না চান বা কাস্টম টাইপ ব্যবহার করতে চান, তা @JsonTypeInfo কনফিগার করে করা যায়।
  3. Interface বা Abstract Class: Shape যদি ইন্টারফেস হয়, তাহলেও একইভাবে কাজ করবে।
  4. Collection Handling: Collection টাইপের অবজেক্ট Serialize বা Deserialize করার সময় সঠিক টাইপ নিশ্চিত করতে হবে।

5. Use Cases

  • Inheritance Models: যখন একাধিক চাইল্ড ক্লাস প্যারেন্ট ক্লাসের মাধ্যমে এক্সপোজ করতে হয়।
  • API Development: REST API-তে ডায়নামিক ডেটা টাইপ হ্যান্ডল করতে।
  • Configuration Systems: কনফিগারেশন ফাইলের ডেটা বিভিন্ন সাবটাইপে বিভক্ত থাকলে।

Polymorphic Data Structure হ্যান্ডল করার জন্য Jackson একটি শক্তিশালী এবং সহজ সরঞ্জাম। @JsonTypeInfo এবং @JsonSubTypes ব্যবহার করে JSON ডেটায় টাইপের তথ্য যোগ করা যায়, যা সহজেই Serialize এবং Deserialize করা সম্ভব।

আরও উদাহরণ বা নির্দিষ্ট কোনো সমস্যার সমাধান জানতে চাইলে জানাতে পারেন! 😊

Content added By
Promotion

Are you sure to start over?

Loading...