• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

zapis i odczyt z pliku przez gson listy różnych obiektów

Object Storage Arubacloud
+1 głos
341 wizyt
pytanie zadane 28 sierpnia 2021 w Java przez Lulex Użytkownik (820 p.)

Cześć, chciałem ogarnąć gson'a i napotkałem problem przy odczycie Listy obiektów z pliku.

Mam klasę abstrakcyjną Shape, oraz klasy Circle, Rectangle, Square, które ją rozszerzają. Chciałem zapisać List<Shape> do pliku file.json - to działa, jednak przy imporcie/ odczycie wywala błąd 

Failed to invoke public Shape() with no args

 

Metody:

 public static List<Shape> importShapeListFromJsonWithGson(String path) throws IOException {
        Type listType = new TypeToken<List<Shape>>() {}.getType();
        Gson gson = new GsonBuilder().create();
        Reader reader = new FileReader(path);
        List<Shape> result = gson.fromJson(reader, listType);
        return result;
    }

    public static void exportShapeListToJsonWithGson(List<Shape> list, String path) throws IOException {
        Writer writer = new FileWriter(path);
        Gson gson = new GsonBuilder().create();
        gson.toJson(list, writer);
        writer.close();
    }

 

W Shape mam już zarówno kontruktor bezargumentowy, jak i z argumemtem 

@Data
@ToString
public abstract class Shape {
    private Type typeOfShape;
    public Shape() {}
    public Shape(Type typeOfShape) {
        this.typeOfShape = typeOfShape;
    }
    public abstract double countArea();

    public abstract double countCircuit();

Jednak dalej to nie działa... da się to w ogóle zrobić z abstrakcyjną klasą?

1 odpowiedź

+1 głos
odpowiedź 28 sierpnia 2021 przez Wiciorny Ekspert (269,590 p.)

Czym jest klasa abstrakcyjna? 

To Ci odpowie w czym jest problem, myślisz dobrze, jednak źle troszke to stosujesz spójrz na twoje mapowanie typu 

new TypeToken<List<Shape>>() {}.getType();

wywołujesz pobranie typu dla Szape, i utworzenie obiektu z typem Shape... a klasa abstrakcyjna na to nie zezwala, skąd Java, ma wiedzieć, tzn TypeToken, że obiekty zapisywane wewnątrz są "Kołem, Kwadratem etc" ? 
Równie dobrze mógłbyś mieć tam zapisane śmieci elipsy, proste ... albo zwierzęta, próbująć to ubrać w opakowanie "Shape" 
Aby deserializować takie obiekty, możesz albo korzystać z Object Mappera on ma lepszy zestaw metod do tego 

Druga opcja to napisanie własnego mappera, spróbuj : 
 

public class ShapeDeserialize implements JsonDeserializer<Shape> {
    private String shapeTypeElementName;
    private Gson gson;
    private Map<String, Class<? extends Shape>>shapeTypeRegistry;

    public ShapeDeserializer(String shapeTypeElementName) {
        this.shapeTypeElementName = shapeTypeElementName;
        this.gson = new Gson();
        this.shapeTypeRegistry = new HashMap<>();
    }

    public void registerBarnType(String shapeTypeName, Class<? extends Shape> shapeType) {
        shapeTypeRegistry.put(shapeTypeName,shapeType);
    }

    public Shape deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) {
        JsonObject shapeObject = json.getAsJsonObject();
        JsonElement shapeTypeElement = shapeObject.get(shapeTypeElementName);

        Class<? extends Shape> shapeType= shapeTypeRegistry.get(shapeTypeElement.getAsString());
        return gson.fromJson(shapeObject, shapeType);
    }
}

i jego wykorzystanie, a ostatnia opcja to zapoznanie się z jsonem-  i jego adnotacjami w zakresie Inheritance 

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "_type")
@JsonSubTypes({
  @JsonSubTypes.Type(value = Kwadrat.class, name = "_type"),
  @JsonSubTypes.Type(value = Trojkat.class, name = "_type"),
})

gdzie określasz jakie typy dziedziczą, rozszerzają twój kształt 

komentarz 29 sierpnia 2021 przez Lulex Użytkownik (820 p.)

Dzięki za podpowiedź, faktycznie, problem leży z rozpoznaniem obiektów. Zrobiłem to objectMapperem - tam nie ma problemów, import fajnie działa, jednak czy można zmodyfikować to w jaki sposób on to zapisuje? Chodzi mi o to, że plik wygląda w ten sposób:

[
  [
    "ShapeService.models.Circle",
    {
      "radius": 3.0
    }
  ],
  [
    "ShapeService.models.Circle",
    {
      "radius": 5.0
    }
  ],
  [
    "ShapeService.models.Rectangle",
    {
      "a": 1.0,
      "b": 2.0
    }
  ],

i czy dałoby radę wywalić część "ShapeService.models." aby zostały same nazwy klas?

komentarz 29 sierpnia 2021 przez Wiciorny Ekspert (269,590 p.)

Pewnie, że jest to do zrobienia, domyślnie używa własnego serializera.
Musisz podać mu "custom serializer', a mozesz go napisac jako własną klase... np tak 

public class ItemSerializer extends StdSerializer<Shape> {
    
    public ItemSerializer() {
        this(null);
    }
  
    public ItemSerializer(Class<Item> t) {
        super(t);
    }

    @Override
    public void serialize(
      Shape shape, JsonGenerator jgen, SerializerProvider provider) 
      throws IOException, JsonProcessingException {
 
        jgen.writeStartObject();
        jgen.writeNumberField("Radius: ", shape.radius);
        // tutaj dalej mozesz dodawac pola jak dormalny json 
        jgen.writeEndObject();
    }
}

następnie jeśli zdefiniowałeś własny mapper,

to musisz teraz napisać " że podczas object mappera z niego korzystasz, czyli inicjalizujesz object mapper tym serializerem i deserializerem 

Rejestrujesz to np tak 
 

ObjectMapper mapper = new ObjectMapper();

SimpleModule module = new SimpleModule();
module.addSerializer(Shape.class, new ItemSerializer());
mapper.registerModule(module);

i masz reretrowana klase w aktualnym module, dla mapera. 
maperów moze byc kilka jako instancje i rózne moduły

aby wypisac obiekt, np najpierw go tworzysz a ostatecznie uzyskujesz go tak 

String serializowanyKsztalt= mapper.writeValueAsString(shapeObject);

pozostaje dodanie oznaczenia, jak ma byc serializowany shape 

@JsonSerialize(using = ItemSerializer.class)
public class Shape {
    // twoja klasa oznaczona adnotacja 
}

ostatecznie uzyskujesz to co chcesz, z tym że to jak zdefiniujesz wygląd zależy od implementacji klasy ItemSerializer i tego jak ma wypisywac obiekt,  jest to dosyc dowolne

Podobne pytania

0 głosów
0 odpowiedzi 261 wizyt
pytanie zadane 29 czerwca 2017 w Java przez XtrEmE Nowicjusz (200 p.)
+1 głos
1 odpowiedź 378 wizyt
pytanie zadane 3 sierpnia 2021 w JavaScript przez xjafajx Użytkownik (740 p.)
0 głosów
1 odpowiedź 189 wizyt
pytanie zadane 26 grudnia 2020 w JavaScript przez sKodowany Obywatel (1,150 p.)

92,539 zapytań

141,382 odpowiedzi

319,479 komentarzy

61,928 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...