Reputation: 31
First of all, I'd like to say this is for a university project.
I have 3 classes. Order
abstract class and Delivery
and DineIn
classes which inherits from Order.
I am using Gson to serialize/deserialize the child classes but I have run into a bit of a problem. The Order class has a field orderType
which gson uses to determine type of order it is, DineIn or Delivery.
Serialization is working just fine. The problem is that whenever I try to deserialize, the type field value is not read and is always set as null even though it is present in the JSON file. This happens when there are a lot of fields in Order because when I tried testing this program on a smaller scale with the Order class just having 2 fields (orderType and orderNo) everything worked just fine. I don't what I am doing wrong. I have tried searching on this site and am almost always coming across suggestions to make custom type adapters and serializers but we haven't studied about them in university and I don't want to use them (the instructor deducts marks for using anything he hasn't taught, I almost failed a course I took from him last time because I used things he hadn't taught. He doesn't seem to have a problem with third-party libraries though).
The code:
public class Main {
public static final List<Order> ordersList = read();
public static void main(String[] args) {
System.out.println(ordersList.get(0).getOrderType());
System.out.println(ordersList.get(0) instanceof DineIn ? "DineIn": "Delivery");
}
private static List<Order> read(){
List<Order> ordersList = new ArrayList<>();
Type type = new TypeToken<ArrayList<Order>>() {
}.getType();
RuntimeTypeAdapterFactory<Order> adapter = RuntimeTypeAdapterFactory.of(Order.class, "orderType")
.registerSubtype(DineIn.class)
.registerSubtype(Delivery.class);
Gson gson = new GsonBuilder().registerTypeAdapterFactory(adapter).create();
JsonReader ordersJsonReader;
try {
ordersJsonReader = new JsonReader(new FileReader("orders.json"));
List<Order> tempOrdersList = gson.fromJson(ordersJsonReader, type);
if (tempOrdersList != null) ordersList = tempOrdersList;
ordersJsonReader.close();
} catch (IOException e) {
e.printStackTrace();
}
return ordersList;
}
}
abstract class Order {
private final int orderNumber;
private final String date, customerName;
private final int discountRate;
private final String paymentMethod;
private String orderStatus;
private int grossTotal = 0;
private double netTotal = 0;
private int totalItems = 0;
protected final String orderType;
public abstract String getOrderType();
public abstract double getExtraCharges();
public Order(int orderNumber, String date, String customerName, int discountRate, String paymentMethod, String orderStatus, int grossTotal, double netTotal, int totalItems, String orderType) {
this.orderNumber = orderNumber;
this.date = date;
this.customerName = customerName;
this.discountRate = discountRate;
this.paymentMethod = paymentMethod;
this.orderStatus = orderStatus;
this.grossTotal = grossTotal;
this.netTotal = netTotal;
this.totalItems = totalItems;
this.orderType = orderType;
}
}
class DineIn extends Order {
private double serviceCharges = 150;
public DineIn(int orderNumber, String date, String customerName, int discountRate, String paymentMethod, String orderStatus, int grossTotal, double netTotal, int totalItems) {
super(orderNumber, date, customerName, discountRate, paymentMethod, orderStatus, grossTotal, netTotal, totalItems, "DineIn");
}
@Override
public String getOrderType() {
return orderType;
}
@Override
public double getExtraCharges() {
return serviceCharges;
}
}
class Delivery extends Order {
private double deliveryCharges = 100;
public Delivery(int orderNumber, String date, String customerName, int discountRate, String paymentMethod, String orderStatus, int grossTotal, double netTotal, int totalItems) {
super(orderNumber, date, customerName, discountRate, paymentMethod, orderStatus, grossTotal, netTotal, totalItems, "Delivery");
}
@Override
public String getOrderType() {
return orderType;
}
@Override
public double getExtraCharges() {
return deliveryCharges;
}
}
The JSON:
[
{
"serviceCharges": 150.0,
"orderNumber": 1,
"date": "12/12/2021",
"customerName": "Ali",
"discountRate": 15,
"paymentMethod": "Card",
"orderStatus": "Preparing",
"grossTotal": 5000,
"netTotal": 4500.0,
"totalItems": 14,
"orderType": "DineIn"
}
]
Upvotes: 2
Views: 750
Reputation: 2981
In your code you have a hierarchy where DineIn
and Delivery
extend from Order
. The way the orderType
field is set is through an explicit String argument in the super()
constructor.
However, Gson does not use the constructor to instantiate the objects. It uses a special no-argument constructor and populates the values via reflection: https://stackoverflow.com/a/40442037/9698467
In this specific case the problem comes from the RuntimeTypeAdapterFactory
, which removes the orderType
field from the JSON that it reads. The source code here confirms that: https://github.com/google/gson/blob/86d88c32cf6a6b7a6e0bbc855d76e4ccf6f120bb/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java#L202
As @fluffy suggested newer versions of the library include the maintain
flag, which should allow for the field to be preserved: https://github.com/google/gson/blob/c1e7e2d2808b042cbe47ca31869ee6ccc62c5417/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java#L214
Upvotes: 1