Forum: PC-Programmierung [java] xstream Vector<InterfaceClass>


von Vlad T. (vlad_tepesch)


Lesenswert?

Hi, ich hofe mir kann jemand helfen.
Ich benutze xstream zur Serialisierung meiner Daten.
Jetzt habe ich ein Problem, dass ich einen Vector habe, der als Typ ein 
Interface hat.

1
public interface IProps{}
2
3
@XStreamAlias("entry")
4
public class CounterField implements IProps{
5
    public CounterField(String description, int count) {
6
       this.description = description;
7
       this.count = count;
8
    }
9
    public String description;
10
    public int    count;
11
}
12
13
@XStreamAlias("list")
14
public class CounterFieldVector  implements IProps
15
{
16
  public CounterFieldVector()
17
  {
18
    fields = new Vector<IProps>();
19
  }
20
21
  @XStreamAlias("elements")
22
  public Vector<IProps> fields;
23
24
}

es soll noch mehrere Implementierungen von IProps geben.
Das ganze kann also beliebig geschachtelt werden.

raus kommt folgendes:
1
    <elements>
2
      <SimpleCounterApp.CounterField>
3
        <description>Example1</description>
4
        <count>0</count>
5
      </SimpleCounterApp.CounterField>
6
      <SimpleCounterApp.CounterField>
7
        <description>Example2</description>
8
        <count>1</count>
9
      </SimpleCounterApp.CounterField>
10
      <SimpleCounterApp.CounterField>
11
        <description>Example3</description>
12
        <count>2</count>
13
      </SimpleCounterApp.CounterField>
14
    </elements>

seltsamerweise ignoriert er die Aliase für die Klassen.

hätte aber gern, dass er diesen benutzt
im obigen Fall halt:
1
    <elements>
2
      <entry>
3
        <description>Example1</description>
4
        <count>0</count>
5
      </entry>
6
      <entry>
7
        <description>Example2</description>
8
        <count>1</count>
9
      </entry>
10
      <entry>
11
        <description>Example3</description>
12
        <count>2</count>
13
      </entry>
14
    </elements>

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Das Problem wird sein das "entry" ein reservierter Bezeichner ist (wird 
für maps verwendet). Selbiges könnte ein Problem bei "list" und 
"elements" auch auftreten. Hast du es mal versucht den Alias direkt bei 
der Xstream instanz einzusetzen?

Btw: Ein leeres Interface ist meistens nicht sehr sinnvoll, denk nochmal 
drüber nach ob z.B. wenigstens Elementare Funktionen wie "getName" o.ä. 
ins Interface wandern sollten.

von Vlad T. (vlad_tepesch)


Lesenswert?

Läubi .. schrieb:
> Das Problem wird sein das "entry" ein reservierter Bezeichner ist (wird
> für maps verwendet). Selbiges könnte ein Problem bei "list" und
> "elements" auch auftreten.

auch wenn ich die Namen komplett ändere, tut sich nix.

Läubi .. schrieb:
> Hast du es mal versucht den Alias direkt bei
> der Xstream instanz einzusetzen?

wie geht das?
In den Beispielen wird es immer nur über Annotations gemacht.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Vlad Tepesch schrieb:
> auch wenn ich die Namen komplett ändere, tut sich nix.
Vielleicht liegts doch an deiner komischen Struktur ;)

> wie geht das?
> In den Beispielen wird es immer nur über Annotations gemacht.
1
XStream xstream = new XStream();
2
xstream.alias("ArrayList", ArrayList.class);
3
xstream.alias("Film", Film.class);
4
xstream.alias("Gruppe", Gruppe.class);
Ansosnten geht ein Alias glaub ich auch nur für Klassen und nicht für 
Felder.

von Vlad T. (vlad_tepesch)


Lesenswert?

Läubi .. schrieb:
> Vielleicht liegts doch an deiner komischen Struktur ;)

wieso komisch?

Ist doch das Grundkonzept der Polymorphie.
Collection vom Typ der Basisklasse (bzw. in dem Fall Interface (habs 
auch schon mit Basisklasse, anstatt Interface bersucht)) mit Instanzen 
von abgeleiteten Klassen.

Läubi .. schrieb:
> Ansosnten geht ein Alias glaub ich auch nur für Klassen und nicht für
> Felder.

nö, das geht für beides.

Wenn ich eine class A als classA annotiere steht in dem XML auch classA.
genauso, wenn ich deren Member int b als intB annotiere.
Außerdem will ich ja auch, dass Klassen der Alias der Klasse benutzt 
wird.


Ich glaub ich werde mal an die Mailinglist posten.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Vlad Tepesch schrieb:
> wieso komisch?
Weil deine "Basis" IProps nichts enthält, d.h. du musst sowieso jedes 
mal die konkrete Klasse suchen hast also keinen Vorteil gegenüber als 
wenn du das ganze von Object ableitest.

D.h. für jede Funktion die ein IProps verarbeiten kann, kann damit 
nichts anfangen weil keine einzige Basismethode vorhanden ist. Wenn aber 
alle IProps Objekte nichts gemeinsam haben handelt es sich streng 
genommen nicht um ein
> Grundkonzept der Polymorphie.

> Läubi .. schrieb:
>> Ansosnten geht ein Alias glaub ich auch nur für Klassen und nicht für
>> Felder.
>
> nö, das geht für beides.
Gut war mir neu, ob man das so weit customizen will/soll muß jeder für 
sich entscheiden ;)

Übrigens sollte man keine "Implemntationsdetails" in den Klassennamen 
schieben. Das CounterFieldVector ein Vektor ist ist eigentlich 
uninteressant (wieso erbt es dann eigentlich nicht von Vektor wenn du 
den einzigen Member der ein Vektor ist auch noch public deklarierst?), 
ebenso das IProps ein Interface ist.

> Ich glaub ich werde mal an die Mailinglist posten.
Wenn du Vektor erweiterst hättest du glaub ich sowieso deine gewünscht 
Struktur ganz ohne Alias.

von Vlad T. (vlad_tepesch)


Lesenswert?

Läubi .. schrieb:
> Weil deine "Basis" IProps nichts enthält, d.h. du musst sowieso jedes
> mal die konkrete Klasse suchen hast also keinen Vorteil gegenüber als
> wenn du das ganze von Object ableitest.

wenn ich das Interface weglasse, könnte ich ja beliebige Objekte da rein 
stecken.
Das soll eigentlich nur ein Marker-Interface sein.

Zu jeder von IProps abgeleiteter Klasse, gibt es dann noch einen 
Handler, der mit den Daten ein Gui-Element baut.

Nach Deserialisierung des Baumes aus IProps. wird für jeden Knoten ein 
entsprechender Handler erzeugt, der die zugehörige GUI erzeugt und sich 
wiederum darum kümmert, dass seine Kinder, falls welche da, gebaut 
werden.

Das ganze funktioniert über eine Art Funktion, die ein Objekt vom IProp 
Interface bekommt, dessen wahren Klasse erfragt und einen Objekt 
Klassenname+"Handler" erzeugt.

Diesen Umweg habe ich gemacht, da die Serialisierung sonst zu viele 
Daten enthalten hätte, die sie gar nicht enthalten soll.
So hat man klare Strukturierte kleine Klassen, die das Datenfile 
abbilden.

Läubi .. schrieb:
> Gut war mir neu, ob man das so weit customizen will/soll muß jeder für
> sich entscheiden ;)

das macht höchstens Sinn, wenn man ein Member intern nicht umbenennen 
möchte, oder kann, weil man sich an irgendwelche Benamungs-Richtlinen 
halten muss.

Läubi .. schrieb:
> Übrigens sollte man keine "Implemntationsdetails" in den Klassennamen
> schieben. Das CounterFieldVector ein Vektor ist ist eigentlich
> uninteressant
nach außen hin ist es quasi eine Liste von Feldern. Der Name ist einfach 
blöd gewählt.

> (wieso erbt es dann eigentlich nicht von Vektor wenn du
> den einzigen Member der ein Vektor ist auch noch public deklarierst?),
> ebenso das IProps ein Interface ist.
das hat noch komischere Ausgaben bei der serialisierung zur folge.
Da werden implementierungsdetails der Vectorklasse mit rausgeschrieben, 
die eigentdlich komplette interna sind, zB reservierter Platz im Vector.
Das würde ich eigentlich als Bug in der Vector-Klasse sehen, dass das 
nicht als transient deklariert ist.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Vlad Tepesch schrieb:
> wenn ich das Interface weglasse, könnte ich ja beliebige Objekte da rein
> stecken.
Jupp das könntest du. Da sich aber ein Objekt von einem welches ein 
IProp implementiert in keiner Weise unterscheidet wäre das fast kein 
Beinbruch.
> Das soll eigentlich nur ein Marker-Interface sein.
Leere Interfaces würde ich trotzdem vermeiden.

> Zu jeder von IProps abgeleiteter Klasse, gibt es dann noch einen
> Handler, der mit den Daten ein Gui-Element baut.
Wäre es dann nicht schlau wenigstens sowas wie "getHandler()" oder so 
dem IF hinzuzufügen? Jedes IProps sollte ja hoffentlich wissen welcher 
Handler hier zuständig ist und so würde:
> Nach Deserialisierung des Baumes aus IProps. wird für jeden Knoten ein
> entsprechender Handler erzeugt, der die zugehörige GUI erzeugt und sich
> wiederum darum kümmert, dass seine Kinder, falls welche da, gebaut
> werden.
sicherlich einfacher ablaufen. Ggf könnte man dann ein Generic Typ dem 
Interface hinzufügen falls man den Typ des handlers noch spezieller 
festlegen möchte.

> Das ganze funktioniert über eine Art Funktion, die ein Objekt vom IProp
> Interface bekommt, dessen wahren Klasse erfragt und einen Objekt
> Klassenname+"Handler" erzeugt.
Das "doofe" an dem Konzept ist nur: Fügst du eine neue IProps Klasse 
hinzu mußt du diese Funktion ändern, ändert sich die Implementation mußt 
du diese Klasse ebenfalls ändern, sowohl für die Klasse als auch für den 
Handler. Daher würde ich diese Aufgabe an das Objekt selbst (bzw 
Interface) deligieren.

> Diesen Umweg habe ich gemacht, da die Serialisierung sonst zu viele
> Daten enthalten hätte, die sie gar nicht enthalten soll.
Daten sollten man natürlich vom View trennen, dann sollten sich hier 
keine Probleme ergeben.

> nach außen hin ist es quasi eine Liste von Feldern. Der Name ist einfach
> blöd gewählt.
Falls du die syncronizierung von Vektor nicht brauchst würde ich auf 
List gehne (z.B. ArrayList).
Die Frage ist halt ob du wirklich eine eigene Klasse brauchst oder es 
ein
1
List<IProps> myList = new ArrayList<IProps>()
Nicht auch tun würde.
>> (wieso erbt es dann eigentlich nicht von Vektor wenn du
>> den einzigen Member der ein Vektor ist auch noch public deklarierst?),
>> ebenso das IProps ein Interface ist.
> das hat noch komischere Ausgaben bei der serialisierung zur folge.
> Da werden implementierungsdetails der Vectorklasse mit rausgeschrieben,
> die eigentdlich komplette interna sind, zB reservierter Platz im Vector.
> Das würde ich eigentlich als Bug in der Vector-Klasse sehen, dass das
> nicht als transient deklariert ist.
Nein! Das sind (für den Vektor) wichtige Informationen! Du kannst ohne 
diese zwar einen (ähnlichen) Vektor erzeugen dieser würde sich aber 
unterscheiden im Verhalten! Wenn du die speziellen Eigenschaften von 
Vektor nicht benötigst würde ich wie gesagt einfach ArrayList benutzen.

> das hat noch komischere Ausgaben bei der serialisierung zur folge.
Allgemein würde ich mir nicht zu viele Gedanken um die Ausgabe machen. 
Solange das ganze nur von Maschinen erzeugt/gelesen werden soll ist das 
ganze recht unkritisch, da sollte man nicht zuviel Ehrgeiz reinstecken.

von Vlad T. (vlad_tepesch)


Lesenswert?

Läubi .. schrieb:
> Das "doofe" an dem Konzept ist nur: Fügst du eine neue IProps Klasse
> hinzu mußt du diese Funktion ändern, ändert sich die Implementation mußt
> du diese Klasse ebenfalls ändern, sowohl für die Klasse als auch für den
> Handler. Daher würde ich diese Aufgabe an das Objekt selbst (bzw
> Interface) deligieren.

Nein, muss nicht. da der tatsächliche Klassenname des Interfaces in 
Erfahrung gebracht wird. Aus diesem Namen wird dann der Namen des 
Handlers hergeleitet und nach einer Klasse dieses Namens gesucht. 
Anschließend wird noch der überprüft ob es einen Konstruktor mit diesem 
speziellen IProps als Parameter gibt und dieser aufgerufen.

Es muss also nur sichergestellt sein, dass der Handler einen bestimmten 
Namen hat.

Aber du hast recht. ein
public IHandler createHandler(IProps);
wär einfacher.


Läubi .. schrieb:
> Falls du die syncronizierung von Vektor nicht brauchst würde ich auf
> List gehne (z.B. ArrayList).
Da hast du auch recht. Macht wahrscheinlich mehr Sinn. Bin von C++ 
std::vector gewohnt und hab nicht länger gesucht, als ich einen Vector 
gefunden habe.

Läubi .. schrieb:
> Nein! Das sind (für den Vektor) wichtige Informationen! Du kannst ohne
> diese zwar einen (ähnlichen) Vektor erzeugen dieser würde sich aber
> unterscheiden im Verhalten! Wenn du die speziellen Eigenschaften von
> Vektor nicht benötigst würde ich wie gesagt einfach ArrayList benutzen.

wieso sollte es wichtig sein, wieviel Platz der Vektor schon im 
Vorhinein reserviert hat? Das sind doch Interna, die nur das 
Memory-Management betreffen.

Läubi .. schrieb:
> Allgemein würde ich mir nicht zu viele Gedanken um die Ausgabe machen.
> Solange das ganze nur von Maschinen erzeugt/gelesen werden soll ist das
> ganze recht unkritisch, da sollte man nicht zuviel Ehrgeiz reinstecken.

genau das ist es ja.
Diese Daten beschreiben die Konfiguration der Gui.
Da das ganze recht klein bleiben soll, will ich keine Gui bauen, um die 
Struktur aufzubauen.
Daher sollte der Output/Input möglichst einfach schreibbar und lesbar 
sein.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Vlad Tepesch schrieb:
> Nein, muss nicht. da der tatsächliche Klassenname des Interfaces in
> Erfahrung gebracht wird. Aus diesem Namen wird dann der Namen des
> Handlers hergeleitet und nach einer Klasse dieses Namens gesucht.
autsch ;) Das geht zwar ist aber höchst unschön, gerade wenn du 
irgenwann mal auf ein automatische Refacotoring zurückgreifen willst, 
ebenso verbaust du dir so die Möglichkeit das der Compiler im Vorfelde 
dir Fehler mitteilt.

> Aber du hast recht. ein
> public IHandler createHandler(IProps);
> wär einfacher.
Jupp und weniger fehleranfällig. Auch hast du so die Möglichkeit die 
Implementierung (z.B. anhand von Konfigparametern) einfach 
auszutauschen.

> Läubi .. schrieb:
>> Falls du die syncronizierung von Vektor nicht brauchst würde ich auf
>> List gehne (z.B. ArrayList).
> Da hast du auch recht. Macht wahrscheinlich mehr Sinn. Bin von C++
> std::vector gewohnt und hab nicht länger gesucht, als ich einen Vector
> gefunden habe.
Es lohnt sich aber. Außerdem kannst du in Java ohne Probleme immer auf 
die kleinste Basisklasse (in diesem Falle z.B. List) gehen. Ob du dann 
konkret einen Vektor, eine Arraylist, oder Linkedlist nimmst ist egal.

> wieso sollte es wichtig sein, wieviel Platz der Vektor schon im
> Vorhinein reserviert hat? Das sind doch Interna, die nur das
> Memory-Management betreffen.
Ein Vektor hat folgende (interne) Größen die für ihn wichtig sind:
- elementCount: Die Anzahl gültiger Elemente
- capacityIncrement: die Anzahl neu zu alloziierender Elemente sobald 
der Vektor einmal zu klein wurde.
Und (vieleicht) auch noch die initialCapacity welche der Vektor 
wenigstens haben soll (zu Anfang), und die aktuelle Größe des Buffers 
(welche man mit ensureCapacity ggf vor dem serialisieren erzwungen hat). 
Diese Werte müssen aber wieder hergestellt werden um den Zustand des 
Objektes zu erhalten!

> genau das ist es ja.
> Diese Daten beschreiben die Konfiguration der Gui.
Schon mal fertige Frameworks angeschaut?
http://www.google.de/search?q=java+gui+XML
Nicht das du das Rad unötigerweise neu erfindest ;)

> Daher sollte der Output/Input möglichst einfach schreibbar und lesbar
> sein.
Wenn das ganze wirklich eine (handkonfigurierbare) Einstellungsdatei 
werden soll, würde ich ggf garnicht über Serialisierung gehen. Da hast 
du nämlich das Problem das diese sofort fehlschlägt wenn der User was 
"vermurkst" hat.
Wenn es unbedingt XML sein muß kannst du dies auch per SAX parsen und 
dann deine Struktur völlig frei wählen.

von Vlad T. (vlad_tepesch)


Lesenswert?

ich habe die Ursache gefunden:


Ich habe vergessen die Liste mit dem processAnnotations zu erweitern.
 *Kopf gegen die Wand schlag*


Läubi .. schrieb:
> Schon mal fertige Frameworks angeschaut?
> http://www.google.de/search?q=java+gui+XML
> Nicht das du das Rad unötigerweise neu erfindest ;)
Ich brauche nicht viel Spielraum bei dem ganzen.
Außerdem sollen in der Struktur auch noch Daten abgelegt werden.
Die Konfiguration ist also eigentlich nur ein Template.
So wie es jetzt ist (nach Hinzufügen von processAnnotations), 
funktioniert es bestens.
Den Vector werde ich dennoch durch ArrayList ersetzen und ein 
createHandler einbauen ;).

Läubi .. schrieb:
> Wenn es unbedingt XML sein muß kannst du dies auch per SAX parsen und
> dann deine Struktur völlig frei wählen.
Ist aber mehr Aufwand.
Ich wollte halt einfach "nur" schnell eine Datenstruktur abspeichern und 
wieder laden, ohne was dazu tun zu müsssen. xstream sah da recht passend 
aus.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.