XML Datei elegant verarbeiten

Status
Nicht offen für weitere Antworten.

multiholle

Aktives Mitglied
Für einen MP3 Player will ich die GUI flexibel gestaltbar machen und das aktuelle Layout in einer XML-Datei speichern. Da in der XML-Datei nur Strings gespeichert werden ist die Verarbeitung relativ unhandlich. Vergleiche sind schwer durchzuführen und dass Umwandeln von Integer und String nervt. Geht das nicht eleganter?

[highlight="java"]
public class PropertyDialogXML {
public static void savePreferences(SplitablePanelView panel) {
try {
XMLOutputFactory factory = XMLOutputFactory.newInstance();
XMLStreamWriter writer = factory.createXMLStreamWriter(new FileOutputStream("properties.xml"));

writer.writeStartDocument();

saveSplitPanelView(panel, writer);

writer.writeEndDocument();
writer.close();
}
catch (Exception e) {
e.printStackTrace();
}
}

private static void saveSplitPanelView(Component component, XMLStreamWriter writer) throws Exception{
String elementName = component.getClass().getSimpleName();

// SplitablePanelView
if (component instanceof SplitablePanelView) {
SplitablePanelView panel = (SplitablePanelView) component;
if (panel.getComponents().length == 0) {
writer.writeStartElement("Empty");
writer.writeEndElement();
return;
}
saveSplitPanelView(panel.getComponent(0), writer);

// JSplitPane
} else if (component instanceof JSplitPane) {
JSplitPane pane = (JSplitPane) component;

writer.writeStartElement("JSplitPane");
writer.writeAttribute("width", String.valueOf(component.getWidth()));
writer.writeAttribute("height", String.valueOf(component.getHeight()));
writer.writeAttribute("orientation", String.valueOf(pane.getOrientation()));
writer.writeAttribute("dividerLocation", String.valueOf(pane.getDividerLocation()));

saveSplitPanelView(pane.getLeftComponent(), writer);
saveSplitPanelView(pane.getRightComponent(), writer);

writer.writeEndElement();

// Player/Libraray/Playlist
} else {
writer.writeStartElement(elementName);
writer.writeAttribute("width", String.valueOf(component.getWidth()));
writer.writeAttribute("height", String.valueOf(component.getHeight()));
writer.writeEndElement();
}
}

public static void loadPreferences(SplitablePanelView panel) {
try {
XMLInputFactory factory = XMLInputFactory.newInstance();
XMLStreamReader parser = factory.createXMLStreamReader(new FileInputStream("properties.xml"));

loadSplitPanelLayout(panel, parser);

// Minimale Größe, bei leerem Inhalt
if (panel.getComponents().length == 0)
panel.setPreferredSize(new Dimension(320,80));

parser.close();
} catch (Exception e) {
e.printStackTrace();
}
}

private static void loadSplitPanelLayout(SplitablePanelView panel, XMLStreamReader parser) throws XMLStreamException {
// Ende
if (!parser.hasNext()) return;

// Kein Startelement
if (parser.getEventType() != XMLStreamConstants.START_ELEMENT) {
parser.next();
loadSplitPanelLayout(panel, parser);
}

// SplitPane
else if (parser.getLocalName().equals("JSplitPane")) {
int orientation = 0;
int dividerLocation = -1;
for (int i = 0; i < parser.getAttributeCount(); i++) {
if (parser.getAttributeLocalName(i).equals("orientation")) {
orientation = Integer.parseInt(parser.getAttributeValue(i));
} else if (parser.getAttributeLocalName(i).equals("dividerLocation")) {
dividerLocation = Integer.parseInt(parser.getAttributeValue(i));
}
}

SplitablePanelView[] returnVal = panel.addSplitPane(orientation, dividerLocation);

parser.next();
loadSplitPanelLayout(returnVal[0], parser);
loadSplitPanelLayout(returnVal[1], parser);
// Leer
} else if (parser.getLocalName().equals("Empty")) {
parser.next();
return;
// Playlist/Player/Library
} else {
int width = 0;
int height = 0;
for (int i = 0; i < parser.getAttributeCount(); i++) {
if (parser.getAttributeLocalName(i).equals("width"))
width = Integer.parseInt(parser.getAttributeValue(i));
else if (parser.getAttributeLocalName(i).equals("height"))
height = Integer.parseInt(parser.getAttributeValue(i));
}
panel.addComponent(parser.getLocalName(), width, height);
parser.next();
return;
}
}
}
[/highlight]
 
Du könntest Dir ein Model überlegen und das dann per JAXB einfach zwischen Java und XML umwandeln.
 
und wenn ich das doch lieber selber umsetzen möchte? der code oben funktioniert wunderbar. ich möchte das speichern nur etwas eleganter lösen und dafür brauch ich ein "model", nach musiKk.

was ist ein model für eine xml-datei und wie erstelle ich soetwas?
 
Nah, ich hatte halt an ein paar Objekte gedacht, die die Information beinhalten. Irgendwelche Listen mit Angaben zu Positionen und Zeugs. Das meinte ich mit Model. Dafür dann ein XML-Schema erstellen und per JAXB umwandeln.
Ich persönlich würde mir allerdings den Vorschlag von foobar wenigstens mal ansehen, vielleicht ist das ja nicht schlecht gelöst.
 
Don't reevent the wheel, unless you wanna learn something about wheels.

Von daher scheiden meines Erachtens Lowlevel-APIs wie DOM/SAX/STAX aus, wenn schon dann zumindest JAXB, Eclipe Link oder EMF.
 
Eine XML Datei sieht jetzt im Moment z. B. so aus. Wie mache ich daraus ein Model?

[highlight="xml"]
<JSplitPane width="668" height="527" orientation="0" dividerLocation="51">
<PlayerView width="666" height="50"></PlayerView>
<JSplitPane width="666" height="470" orientation="1" dividerLocation="401">
<LibraryView width="400" height="468"></LibraryView>
<PlaylistView width="259" height="468"></PlaylistView>
</JSplitPane>
</JSplitPane>
[/highlight]

EDIT: Es gibt ein JSplitPane welches linke und rechts jeweils eine Komponente besitzt zu denen folgdende gehören: ein weiteres JSplitPane, Player, Libraray, ...
 
Zuletzt bearbeitet:
Überleg dir welche Daten du speichern willst und wie die Entities zusammen hängen. Eine allgemeine Antwort darauf wie man ein Model erstellt wirste nicht finden, da das von Fall zu Fall variiert.
 
Was ich speichern will weiß ich. Nur wie mache ich daraus ein Model? Wie sieht sowas aus und wie verarbeit ich das am besten?
 
Also ich habe jetzt eine Allgemeine Klasse für alle Komponenten:
[highlight="Java"]
package main.common;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(namespace = "ComponentSettingsXML")
public class ComponentSettingsXML {
public int getWidth() {
return width;
}

public void setWidth(int width) {
this.width = width;
}

public int getHeight() {
return height;
}

public void setHeight(int height) {
this.height = height;
}


protected int width;
protected int height;
}
[/highlight]

Und als Koponenten zum einen ein SplitablePanel (ähnlich SplitPane)...
[highlight="Java"]
package main.gui;

import java.util.Vector;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

import main.common.ComponentSettingsXML;
import main.playlist.PlaylistSettingsXML;

@XmlRootElement(name = "SplitablePanel")
public class SplitablePanelSettingsXML extends ComponentSettingsXML{
public int getOrientation() {
return orientation;
}

public void setOrientation(int orientation) {
this.orientation = orientation;
}

public int getDividerLocation() {
return dividerLocation;
}

public void setDividerLocation(int dividerLocation) {
this.dividerLocation = dividerLocation;
}

public ComponentSettingsXML getLeftComponent() {
return leftComponent;
}

public void setLeftComponent(ComponentSettingsXML leftComponent) {
this.leftComponent = leftComponent;
}

public ComponentSettingsXML getRightComponent() {
return rightComponent;
}

public void setRightComponent(ComponentSettingsXML rightComponent) {
this.rightComponent = rightComponent;
}


private ComponentSettingsXML leftComponent;
private ComponentSettingsXML rightComponent;
private int orientation;
private int dividerLocation;
}
[/highlight]

... und eine Playlist.
[highlight="Java"]
package main.playlist;

import java.util.Vector;

import javax.xml.bind.annotation.XmlRootElement;

import main.common.ComponentSettingsXML;

@XmlRootElement(name = "Playlist")
public class PlaylistSettingsXML extends ComponentSettingsXML {
public PlaylistSettingsXML() {

}

public PlaylistSettingsXML(PlaylistView view) {
width = view.getWidth();
height = view.getHeight();
tableColumnIds = view.getColumnIdsModel();
}

public void setTableColumnIds(Vector<Integer> tableColumnIds) {
this.tableColumnIds = tableColumnIds;
}

public Vector<Integer> getTableColumnIds() {
return tableColumnIds;
}


private Vector<Integer> tableColumnIds;
}
[/highlight]

Hiermit erzeuge ich die XML-Datei:
[highlight="Java"]
public static void main(String[] args) throws Exception {
PlaylistSettingsXML playlist1 = new PlaylistSettingsXML();
PlaylistSettingsXML playlist2 = new PlaylistSettingsXML();
SplitablePanelSettingsXML panel = new SplitablePanelSettingsXML();
SplitablePanelSettingsXML panel2 = new SplitablePanelSettingsXML();

Vector<Integer> columnIds = new Vector<Integer>() {
{ add(3); add(-1); add(1); add(4); }
};

playlist1.setWidth(200);
playlist1.setHeight(100);
playlist1.setTableColumnIds(columnIds);

playlist2.setWidth(300);
playlist2.setHeight(400);
playlist2.setTableColumnIds(columnIds);

panel.setWidth(500);
panel.setHeight(300);
panel.setOrientation(1);
panel.setDividerLocation(200);
panel.setLeftComponent(playlist1);
panel.setRightComponent(playlist2);

panel2.setWidth(500);
panel2.setHeight(300);
panel2.setOrientation(1);
panel2.setDividerLocation(200);
panel2.setLeftComponent(panel);
panel2.setRightComponent(playlist2);

JAXBContext context = JAXBContext.newInstance(SplitablePanelSettingsXML.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.marshal(panel2, System.out);
}
[/highlight]

Das Ergebnis sieht dann so aus:
[highlight="xml"]
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<SplitablePanel xmlns:ns2="ComponentSettingsXML">
<height>300</height>
<width>500</width>
<dividerLocation>200</dividerLocation>
<leftComponent xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="splitablePanelSettingsXML">
<height>300</height>
<width>500</width>
<dividerLocation>200</dividerLocation>
<leftComponent>
<height>100</height>
<width>200</width>
</leftComponent>
<orientation>1</orientation>
<rightComponent>
<height>400</height>
<width>300</width>
</rightComponent>
</leftComponent>
<orientation>1</orientation>
<rightComponent>
<height>400</height>
<width>300</width>
</rightComponent>
</SplitablePanel>
[/highlight]

Leider kann man jetzt in der XML-Datei nicht mehr erkennen um welche Komponenten es sich bei leftComponent und bei rightComponent handelt. Zudem werden die Eigenschaften der Playlist nicht gespeichert (tableColumnIDs). Was mache ich falsch?

EDIT: Das Problem scheint der Typ von leftComponent und rightComponent zu sein. Da ich hier ComponentSettingsXML gewählt habe werden keine Playlist-spezifischen Eigenschaften abgefragt. PlaylistSettingsXML kann ich aber nicht wählen, da eine Komponente ja auch wieder eine SplitablePanelSettingsXML sein kann :/
 
Zuletzt bearbeitet:
@XmlAttribute

kannst du nutzen um deine Werte height usw. als Attribute anstatt als Element zu hinterlegen.

Galileo Computing :: Java ist auch eine Insel (8. Auflage) – 15.8 Java Architecture for XML Binding (JAXB)

Der Punkt "Hierarchien einsetzen", ziemlich weit unten beschrieben, beschreibt wie man mit Hilfe von @XmlElementRefs und @XmlElementRef( type = ....class ) dein Problem mit den Vererbungen lösen kann. In dem Beispiel bezieht man sich allerdings auf listen, ob das mit einzelnen Objekten auch funktioniert weis ich nicht, aber ein Versuch wäre es Wert.
 
Ich habe jetzt meine Klassen entsprechend dem Link angepasst. Es scheint auch zum Teil zu funktionieren, leider nicht komplett. Bei den SplitablePanelSettingsXML wird für die leftComponent folgendes ausgegeben:
[highlight="XML"]
<splitablePanelSettingsXML> ... </splitablePanelSettingsXML>
[/highlight]
Für die rightComponent hingegen wird dies geschrieben:
[highlight="XML"]
<rightComponent xsi:type="splitablePanelSettingsXML" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> ... </rightComponent>
[/highlight]
Was für die rightComponent ausgegeben wird scheint richtig zu sein. Warum, jedoch ist es bei der leftComponent anders, obwohl sie in der gleichen Klasse gleich definiert untereinander stehen? Aus dem Code der leftComponent wird auch kein Objekt erzeugt beim einlesen.

[highlight="Java"]
public abstract class ComponentSettingsXML {
public int width;
public int height;
}


@XmlRootElement
public class PlaylistSettingsXML extends ComponentSettingsXML {
public @XmlList Vector<Integer> tableColumnIds;
}


@XmlRootElement
@XmlType(propOrder = {"orientation", "dividerLocation", "leftComponent", "rightComponent"})
public class SplitablePanelSettingsXML extends ComponentSettingsXML{
@XmlElementRefs(
{
@XmlElementRef(type = SplitablePanelSettingsXML.class),
@XmlElementRef(type = PlaylistSettingsXML.class)
})

public ComponentSettingsXML leftComponent;
public ComponentSettingsXML rightComponent;

public int orientation;
public int dividerLocation;
}


public class Test {
public static void main(String[] args) throws Exception {
PlaylistSettingsXML playlist1 = new PlaylistSettingsXML();
PlaylistSettingsXML playlist2 = new PlaylistSettingsXML();
SplitablePanelSettingsXML panel0 = new SplitablePanelSettingsXML();
SplitablePanelSettingsXML panel1 = new SplitablePanelSettingsXML();
SplitablePanelSettingsXML panel2 = new SplitablePanelSettingsXML();

Vector<Integer> columnIds = new Vector<Integer>() {
{ add(3); add(-1); add(1); add(4); }
};

playlist1.width = 200;
playlist1.height = 100;
playlist1.tableColumnIds = columnIds;

playlist1.width = 300;
playlist1.height = 400;
playlist1.tableColumnIds = columnIds;

panel0.width = 300;
panel0.height = 400;
panel0.orientation = 1;
panel0.dividerLocation = 200;
panel0.leftComponent = playlist1;
panel0.rightComponent = playlist2;

panel1.width = 500;
panel1.height = 300;
panel1.orientation = 1;
panel1.dividerLocation = 200;
panel1.leftComponent = playlist1;
panel1.rightComponent = playlist2;

panel2.width = 500;
panel2.height = 300;
panel2.orientation = 1;
panel2.dividerLocation = 200;
panel2.leftComponent = panel0;
panel2.rightComponent = panel1;

JAXBContext context = JAXBContext.newInstance(SplitablePanelSettingsXML.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
Writer w = new FileWriter("layout.xml");
m.marshal(panel2, w);
w.close();

Unmarshaller um = context.createUnmarshaller();
SplitablePanelSettingsXML panel = (SplitablePanelSettingsXML) um.unmarshal(new FileReader("layout.xml"));
}
}
[/highlight]

[highlight="XML"]
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<splitablePanelSettingsXML>
<width>500</width>
<height>300</height>
<orientation>1</orientation>
<dividerLocation>200</dividerLocation>
<splitablePanelSettingsXML>
<width>500</width>
<height>300</height>
<orientation>1</orientation>
<dividerLocation>200</dividerLocation>
<playlistSettingsXML>
<width>300</width>
<height>400</height>
<tableColumnIds>3 -1 1 4</tableColumnIds>
</playlistSettingsXML>
<rightComponent xsi:type="playlistSettingsXML" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<width>0</width>
<height>0</height>
</rightComponent>
</splitablePanelSettingsXML>
<rightComponent xsi:type="splitablePanelSettingsXML" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<width>500</width>
<height>300</height>
<orientation>1</orientation>
<dividerLocation>200</dividerLocation>
<playlistSettingsXML>
<width>300</width>
<height>400</height>
<tableColumnIds>3 -1 1 4</tableColumnIds>
</playlistSettingsXML>
<rightComponent xsi:type="playlistSettingsXML">
<width>0</width>
<height>0</height>
</rightComponent>
</rightComponent>
</splitablePanelSettingsXML>

[/highlight]
 
Irgendwie merkwürdig.

In deinem Codestück ist leftComponent Annotiert, rightComponent allerdings nicht.
 
Status
Nicht offen für weitere Antworten.

Zurück
Oben