XML Datei schreiben

clash

Mitglied
Hallo ich bins wieder... Ihr habt mir letztens super geholfen, aber es gibt wieder etwas bei dem ich nicht weiter komme. Ich soll aus einer Klasse Drawing eine XML Datei schreiben. Also die Klasse Drawing ist eine ArrayList von der Klasse Figure. Figure ist eine abstrakte Klasse als Grundmuster für alle konkreten Figuren. Es gibt die Klassen Ellipse und Rectangle welche die Klasse Figure erweitern. Dazu gibt es dann noch die generische Klasse Tupel zur Verwaltung zweier Werte vom Typ T. Dann folgt noch die Klasse Color, um auf Farben zuzugreifen. So die Klassen Color, Tupel, Drawing und Figure haben jetzt noch zusätzlich eine Subklasse bekommen welche namexDOM heißt. Also 4 zusätzliche Klassen.
Die Aufgabe lautet: Entwickeln, implementieren Sie eine Klasse DrawingDOM, mit der eine Zeichnung, ein Objekt der Klasse Drawing mit den 3 Technologien gespeichert und wieder eingelesen werden kann

Ich weiß jetzt nicht ob ich hier den gesamten Code posten soll, deswegen erstmal nur der Abschnitt welcher Probleme macht.
Java:
public static void writeXMLEncoder (Drawing f, String file) {
        try {
            java.beans.XMLEncoder out = new java.beans.XMLEncoder(new FileOutputStream(file));
            out.writeObject(f);
            out.flush();
            out.close();
        } catch (IOException ex) {
            System.err.println(ex.getMessage());
        }
    }
->das wirft mir den Fehler: java.lang.Exception: Encoder: discarding statement Drawing.setCnt(Integer);
Java:
public void setCnt(int cnt) {
        if (cnt >= 0 && cnt <= shapes.size())
        this.cnt= cnt;
    }
Das ist der Code dazu. wenn ich jetzt setCnt lösche, funktioniert meine Klasse Drawing nicht mehr. Ich komme da irgendwie nicht weiter... Ich hoffe ihr könnt mir Tipps geben 🙁
 
XMLEncoder benötigt ein JavaBeans-Objekt, um es zu serialisieren, daher muss man einen öffentlichen Standardkonstruktor (ohne Argumente) definieren. Also ist das Problem vermutlich bei: shapes zu suchen.
 
Zuletzt bearbeitet:
Okay, ich hab den Fehler gefunden, aber eine Datei mit Inhalt schreibt er immer noch nicht. Jedoch bekomme ich keine Fehlermeldung mehr ... :/
 
Zuletzt bearbeitet:
Was ist shapes also die konkrete Klasse(n) hinter dem Interface IFigure - haben die alle einen Standardkonstruktor?
Ja. Die Klasse Rectangle zB sieht so aus:
Java:
public Rectangle(double xpos, double ypos, double width, double height, Color fillcolor, Color linecolor){
            setPosition(xpos, ypos);
            setSize(width, height);
            setColors(fillcolor, linecolor);
        }
     public Rectangle(double xpos, double ypos, double width, double height){
            setPosition(xpos, ypos);
            setSize(width, height);
        }
     public Rectangle(Tupel<Double> position, Tupel <Double>size, Tupel<Color> colors){   
            setPosition(position.x,position.y);
            setSize(size.x,size.y);
            setColors(colors.x,colors.y);
        }
     public Rectangle (Tupel <Double> position, Tupel<Double> size) {;
            setPosition(position.x, position.y);
            setSize(size.x,size.y);
         }
     /*
      * Standardkonstruktor
      */
    
     public Rectangle () {
     }
 
Okay, daran kann ich aber nichts ändern. Ich wüsste nicht wie ich diese Klasse umschreiben kann. Grundsätzlich sollen wir nur die Klasse drawingDOM schreiben, an den anderen Klassen sollte soweit nichts verändert werden außer das hinzugefügen vom Interface Serializable
 
Evtl. kannst du folgendes machen:
Java:
    FileOutputStream fos = new FileOutputStream(...);
    XMLEncoder encoder = new XMLEncoder(fos);
    encoder.setExceptionListener(new ExceptionListener() {
            public void exceptionThrown(Exception e) {
                System.out.println("Exception! :"+e.toString());
            }
    });
    encoder.writeObject(...);
    encoder.close();
    fos.close();
}
So kannst du weitere mögliche Fehlermeldungen sehen.
 
Bildschirmfoto vom 2021-05-21 21-14-47.png
Die Liste kommt an, aber es wird nicht als Object übergeben. Sondern nichts. Das verstehe ich nicht. Wenn ich dazu noch die Funktion
Java:
out.writeObject(f.getShapes)
dazu nehme, bekomme ich in der XML-Datei folgenden Ausdruck:
<object class="java.util.ArrayList">
<void method="add">
<object class="le07.Rectangle"/>
</void>
<void method="add">
<object class="le07.Ellipse"/>
</void>
</object>
</java>
Also wird erkannt, dass es eine Klasse Rectangle und Ellipse gibt. Aber wie kann ich denn auf diese jetzt zugreifen?
Die Ausgabe im Terminal soll identisch zur Ausgabe vom Serializer sein:
Serializer:
c3: Drawing [cnt=2, shapes=[ Umfang = 300.0 Fläche = 5000.0 xpos= 50.0ypos = 20.0 Weite = 100.0 Höhe = 50.0, Umfang = 392.6990816987241 Fläche = 7853.981633974483 xpos= 150.0ypos = 200.0 Weite = 50.0 Höhe = 200.0]]
c1 == c3: true

XMLEncoder:
c4: Drawing [cnt=0, shapes=[]] //genau hier ist das Problem
c1 == c4: false
 
Zuletzt bearbeitet:
Nochmal: es müssen die Anforderungen einer Bean erfüllt sein. Folgender Test:
Java:
public class Main {

    public static void main(String[] args) throws Exception {
        final ArrayList<ITest> list = new ArrayList<>();
        final Test t = new Test();
//        t.setA(0);
//        t.setB(99);
        t.setAB(7,99);
        list.add(t);

        final FileOutputStream fos = new FileOutputStream("test.xml");
        final XMLEncoder encoder = new XMLEncoder(fos);
        encoder.setExceptionListener(e -> System.out.println("Exception! :"+e.toString()));
        encoder.writeObject(list);
        encoder.close();
        fos.close();
    }
}

public interface ITest {}

public class Test implements ITest {
    private int a;
    private int b;

    public int getA() {
        return this.a;
    }
//    public void setA(int a) {
//        this.a = a;
//    }
    public int getB() {
        return this.b;
    }
//    public void setB(int b) {
//        this.b = b;
//    }
    public void setAB(int a, int b) {
        this.a = a;
        this.b = b;
    }
}
Das Ergebnis der auskommentierten Variante ist (ohne setAB(int,int);) genau wie erwartet:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<java version="11.0.11" class="java.beans.XMLDecoder">
 <object class="java.util.ArrayList">
  <void method="add">
   <object class="test.Test">
    <void property="b">
     <int>99</int>
    </void>
   </object>
  </void>
 </object>
</java>

Das Ergebnis nach der Änderung:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<java version="11.0.11" class="java.beans.XMLDecoder">
 <object class="java.util.ArrayList">
  <void method="add">
   <object class="test.Test"/>
  </void>
 </object>
</java>
 
Eine mögliche Lösung für das Problem will ich noch kurz vorstellen. Deine Klasse Rectangle hat einen Konstruktor für alle benötigten Werte. Also ergänze ich mal die Klasse Test um einen Konstruktor für alle Felder:
Java:
public class Test implements ITest {
    private int a;
    private int b;

    public Test() {
        super();
    }

    public Test(int a, int b) {
        this.a = a;
        this.b = b;
    }

...

Jetzt ist es möglich dem XMLEncoder s.g. PersistenceDelegate-Instanzen bekannt zu machen. In dem Beispiel würde es dann so aussehen:
Java:
        final ArrayList<ITest> list = new ArrayList<>();
        final Test t = new Test();
//        t.setA(0);
//        t.setB(99);
        t.setAB(7,99);
        list.add(t);

        final FileOutputStream fos = new FileOutputStream("test.xml");
        final XMLEncoder encoder = new XMLEncoder(fos);
        encoder.setExceptionListener(e -> System.out.println("Exception! :"+e.toString()));
        encoder.setPersistenceDelegate(Test.class, new PersistenceDelegate() {
            @Override
            protected Expression instantiate(Object oldInstance, Encoder out) {
                final Test old = (Test) oldInstance;
                return new Expression(old, old.getClass(), "new", new Object[] {old.getA() , old.getB()});
            }
        });
        encoder.writeObject(list);
        encoder.close();
        fos.close();
Ich hoffe das Prinzip wird deutlich. Es muss für jede mögliche Klasse, welche in der Liste shapes enthalten ist, eine PersistenceDelegate-Instanz registriert werden. Diese muss eine Expression zurückgeben, mit welcher die Klasse vollständig initialisiert werden kann. Das arguments-Array muss dabei alle Werte für den Konstruktor in der richtigen Reihenfolge enthalten.

Das Ergebnis ist dann so:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<java version="11.0.11" class="java.beans.XMLDecoder">
<object class="java.util.ArrayList">
  <void method="add">
   <object class="test.Test">
    <int>7</int>
    <int>99</int>
   </object>
  </void>
</object>
</java>
 
Okay, vielen Dank für die Mühe. Das habe ich auch so teilweise verstanden. Leider bringt mich das nicht weiter. Es hieß wir sollen diesen Code(ais der Klasse FigureDOM):
Java:
public static void writeXMLEncoder(Figure f, String filename) {
        // Write Encoder
        try {
            java.beans.XMLEncoder out = new java.beans.XMLEncoder(new FileOutputStream(filename));
            writeXMLEncoder(f, out);
            out.flush();
            out.close();
        } catch (IOException ex) {
            System.err.println(ex.getMessage());
        }
    }

    /**
     * Speichern (XML) einer Figure in Stream *.xml
     *
     * @param out      - der Stream
     * @param filename - der Dateiname
     */
    public static void writeXMLEncoder(Figure f, java.beans.XMLEncoder out) {
        // Write Encoder
        out.writeObject(f);
        out.writeObject(f.getPosition());
        out.writeObject(f.getSize());
        out.writeObject(f.getColors());
    }
umschreiben und das geht ganz einfach. Was ich nicht nachvollziehen kann und wenn ich das jetzt alles einsetze, was du mir gesagt hast, dann bekomme ich garantiert wieder tausende Fehler im VPL angezeigt.
 
Ich kann es mal probieren, aber ich denke eher nicht nein. Ich hab es jetzt auch so geschafft, dass ich die minimale Punktzahl erreicht habe. Das reicht mir erstmal. Vielen dank, euch!
 

Neue Themen


Zurück
Oben