Как я могу десериализовать строку Json, где подкласс класса объекта включен в строку json, используя библиотеку Java и Jackson

Я очень новичок в Java. У меня есть несколько классов Site, Instances, CloudInstance. Класс Site имеет экземпляры атрибутов, а класс CloudInstance наследует класс Instance. Они как follows-

public class Site extends BaseEntity {
 private String siteName;
 List<instance> instances = Lists.newArrayList();
 }

 public class Instance extends BaseEntity {
 private String instanceId;
 private String name;
 }

 public class CloudInstance extends Instance {
 private String availabilityZone;
 private String instanceType
 }
</instance>

Я десериализую строку json следующим образом -

import com.fasterxml.jackson.databind.ObjectMapper;

 ObjectMapper mapper = new ObjectMapper(); 
 BaseEntity obj = null;
 obj = (BaseEntity) mapper.readValue(jsonStr, Site.class);

Он отлично работает, если мой jsonStr не содержит полей класса "CloudInstance" и содержит экземпляр поля с полями класса экземпляров.

Проблема. Теперь я хочу десериализовать jsonStr, который включает в себя "CloudInstance" classe fiels, а также часть поля "экземпляров" класса "Сайт". Ex jsonStr выглядит следующим образом:

{
 "id": null,
 "siteName": "demo",
 "instances": [
 {
 "instanceId": "i-8c2ee5fc",
 "name": "some-node",
 "availabilityZone": "some-zone",
 "instanceType": "t1.micro" 
 }]
 }

Для вышеупомянутого jsonStr я получаю следующую ошибку

error: Unrecognized field \"availabilityZone\" and error: Unrecognized field \"instanceType\"

С большим количеством, если еще и грязным кодом, я могу получить объект сайта, включая вышеуказанные поля. Но я хочу реализовать для этого чистое решение. Есть ли библиотека, которая может это сделать? Любая помощь id является ценной. Пожалуйста помоги..!!

Заранее спасибо.

4 ответа

То, что вы пытаетесь достичь, называется полиморфной десериализацией. Ваш пример терпит неудачу, потому что Джексону необходимо знать, какой тип экземпляра должен быть построен из JSON и помещен в список экземпляров. Пожалуйста, обратитесь к этой вики-странице для подробного объяснения.

Я сделал вам пример, чтобы продемонстрировать, как он может работать. Я добавил информацию о типе экземпляра в поле @type в представлении JSON. Также я сделал все классы неизменными, используя конструкторы, аннотированные аннотацией @JsonCreator, для создания экземпляров.

public class JacksonPolymorphism {

 public static class BaseEntity {
 private final String id;

 protected BaseEntity(String id) {
 this.id = id;
 }
 }

 public static class Site extends BaseEntity {
 private final String siteName;
 private final List<instance> instances;

 @JsonCreator
 public Site(@JsonProperty("id") String id,
 @JsonProperty("siteName") String siteName,
 @JsonProperty("instances") List<instance> instances) {
 super(id);
 this.siteName = siteName;
 this.instances = instances;
 }

 @Override
 public String toString() {
 return "Site{" +
 "siteName='" + siteName + '\'' +
 ", instances=" + instances +
 '}';
 }
 }

 @JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
 include = JsonTypeInfo.As.PROPERTY,
 property = "@type")
 @JsonTypeName(value = "simple")
 public static class Instance extends BaseEntity {
 private final String name;

 @JsonCreator
 public Instance(@JsonProperty("instanceId") String id,
 @JsonProperty("name") String name) {
 super(id);
 this.name = name;
 }

 @Override
 public String toString() {
 return "Instance{" +
 "name='" + name + '\'' +
 '}';
 }
 }

 @JsonTypeName("cloud")
 public static class CloudInstance extends Instance {
 private final String availabilityZone;
 private final String instanceType;


 public CloudInstance(@JsonProperty("instanceId") String id,
 @JsonProperty("name") String name,
 @JsonProperty("availabilityZone") String availabilityZone,
 @JsonProperty("instanceType") String instanceType) {
 super(id, name);
 this.availabilityZone = availabilityZone;
 this.instanceType = instanceType;
 }

 @Override
 public String toString() {
 return "CloudInstance{" +
 "availabilityZone='" + availabilityZone + '\'' +
 ", instanceType='" + instanceType + '\'' +
 '}';
 }
 }

 public static final String JSON = "{\n" +
 " \"id\": null,\n" +
 " \"siteName\": \"demo\",\n" +
 " \"instances\": [\n" +
 " {\n" +
 " \"@type\": \"cloud\",\n" +
 " \"instanceId\": \"i-8c2ee5fc\",\n" +
 " \"name\": \"some-node\",\n" +
 " \"availabilityZone\": \"some-zone\",\n" +
 " \"instanceType\": \"t1.micro\" \n" +
 " }," +
 " {\n" +
 " \"@type\": \"simple\",\n" +
 " \"instanceId\": \"ABC\",\n" +
 " \"name\": \"FGF\"\n" +
 " }]" +
 " }";
 public static void main(String[] args) throws IOException {
 ObjectMapper mapper = new ObjectMapper();
 mapper.registerSubtypes(CloudInstance.class);
 System.out.println(mapper.readValue(JSON, Site.class));
 }

}
</instance></instance>

Вывод:

Site{siteName='demo', instances=[CloudInstance{availabilityZone='some-zone', instanceType='t1.micro'}, Instance{name='FGF'}]}


Вы можете использовать библиотеку GSON для де-сериализации Json в свой объект класса.

Существует функция gson.fromJson(JSON String) которая преобразует строку json в объект класса.

Вот пример кода:

Gson json = new Gson();
Site site = json.fromJson(jsonStr, Site.class);

Но в вашем коде вы замените

List<instance> instances = Lists.newArrayList(); 
</instance>

эта строка в классе Site with

List<cloudinstance> instances = new ArrayList<cloudinstance>(); 
</cloudinstance></cloudinstance>

Поскольку ваш класс CloudInstance расширяет Instance это означает, CloudInstance класс CloudInstance включает член класса Instance. В соответствии с json вам нужно сделать это, чтобы напрямую вставить объект класса.

Пусть это поможет вам.


У меня всегда были проблемы для десериализации JSON, который содержит объекты List <...> с Jackson, поэтому попробуйте десериализовать с Gson:

https://code.google.com/p/google-gson/

Взгляните на документацию по методам fromJson и toJson.

Надеюсь, это может помочь, С наилучшими пожеланиями


Никогда не было постоянной удачи с Джексоном или Гсоном, вместо этого попробуйте использовать Flex JSON:

JSONSerializer ser = new JSONSerializer();
 String json = ser.deepSerialize(yourObject);
 JSONDeserializer<yourmaintype> der = new JSONDeserializer<yourmaintype>();
 YourMainType mainType = der.deserialize(json); 
</yourmaintype></yourmaintype>

Чтобы это работало, все классы, подверженные сериализации/десериализации, должны выставлять геттеры/сеттеры в соответствии с соглашением Java Beans.

licensed under cc by-sa 3.0 with attribution.