• 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

Aruba Cloud VPS - 50% taniej przez 3 miesiące!
+1 głos
424 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 (278,610 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 (278,610 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 272 wizyt
pytanie zadane 29 czerwca 2017 w Java przez XtrEmE Nowicjusz (200 p.)
+1 głos
1 odpowiedź 562 wizyt
pytanie zadane 3 sierpnia 2021 w JavaScript przez xjafajx Użytkownik (740 p.)
0 głosów
1 odpowiedź 223 wizyt
pytanie zadane 26 grudnia 2020 w JavaScript przez sKodowany Obywatel (1,150 p.)

93,157 zapytań

142,170 odpowiedzi

321,878 komentarzy

62,486 pasjonatów

Advent of Code 2024

Top 15 użytkowników

  1. 224p. - Marcin Putra
  2. 224p. - nidomika
  3. 223p. - dia-Chann
  4. 221p. - ssynowiec
  5. 217p. - Mikbac
  6. 216p. - CC PL
  7. 215p. - Łukasz Piwowar
  8. 212p. - zmmz89
  9. 210p. - Adrian Wieprzkowicz
  10. 208p. - rafalszastok
  11. 206p. - Michal Drewniak
  12. 204p. - Łukasz Eckert
  13. 202p. - rucin93
  14. 200p. - robwarsz
  15. 198p. - TheLukaszNs
Szczegóły i pełne wyniki

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

Wprowadzenie do ITsec, tom 1 Wprowadzenie do ITsec, tom 2

Można już zamawiać dwa tomy książek o ITsec pt. "Wprowadzenie do bezpieczeństwa IT" - mamy dla Was kod: pasja (użyjcie go w koszyku), dzięki któremu uzyskamy aż 15% zniżki! Dziękujemy ekipie Sekuraka za fajny rabat dla naszej Społeczności!

...