# XStream - Implicit Object



## luventas (19. Jul 2012)

Hallo Zusammen,

ich bin neu hier im Forum. Ich habe seit 2 Tagen versucht, ein Problem bei der Serialisierung und Deserialisierung von XML zu Java und Imgekehrt zu lösen und bin bei XStream auf eine Hürde gestoßen, die bisher niemand im Internet beantwortet hat. Auch die Forumssuche hier hat nichts ergeben. Daher hoffe ich, dass jemand hier mir helfen kann:

Ich möchte Java-Objekte aus einer XML Datei befüllen. Hierzu habe ich bereits eine funktionierende Implementation mit dem Simple Framework erzeugt. 
Nun möchte ich das selbe gerne noch mit XStram machen. Hier stoße ich aber auf ein Problem, dass irgendwie unüberwindlich erscheint:

Auszüge aus meiner XML:
[XML]
<rootElement>
	<conversionStatus>READY</conversionStatus>
	<costs>bis 5 Euro</costs>
	<courses>
		<course primary="true">Vorspeisen</course>
		<course>Zwischengänge</course>
		<course>Desserts</course>
	</courses>
	<createdOn>2012-08-01 22:14:17</createdOn>
	<deletedAt>2012-08-11 21:31:12</deletedAt>
</rootElement>
[/XML]

Meine hierzu gehörigen JavaObjekte sehen auszugsweise wie folgt aus:

```
public class RootElement implements Serializable {

	private static final long serialVersionUID = 1L;
	
	@Element(name="conversionStatus", required=true)
	private String conversionStatus;
	
	@Element(name="costs", required=false)
	private String costLevel;
	
	@Element(name="createdOn", required=false)
	private Date createdOn;

	@Element(name="deletedAt", required=false)
	private Date deletedAt;

	@ElementList(type=Course.class, name="courses", required=false)
	private ArrayList<Course> courses;

	public RootElement() {
	}

	public Date getCreatedOn() {
		return createdOn;
	}

	public void setCreatedOn(Date createdOn) {
		this.createdOn = createdOn;
	}

	public ArrayList<Course> getCourses() {
		return courses;
	}

	public void setCourses(ArrayList<Course> courses) {
		this.courses = courses;
	}

	public String getCostLevel() {
		return costLevel;
	}

	public void setCostLevel(String costLevel) {
		this.costLevel = costLevel;
	}

	public Date getDeletedAt() {
		return deletedAt;
	}

	public void setDeletedAt(Date deletedAt) {
		this.deletedAt = deletedAt;
	}
}
```


```
package de.sevenload.domainObjects;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Text;

public class Course implements Serializable {

	private static final long serialVersionUID = 1L;

	@Text
	private String courseName;
	
	@Attribute(required=false, name="primary")
	private String primary;
	
	public Course() {
	}
	
	public Course(@Text String courseName) {
		this.courseName = courseName;
		this.primary = null;
	}
	
	public Course(@Text String courseName, @Attribute(required=false, name="primary", empty="false")String primary) {
		if(primary != null) {
			this.primary = primary;
		}
		this.courseName = courseName;
	}
	
	public String getCourseName() {
		return courseName;
	}

	public void setCourseName(String courseName) {
		this.courseName = courseName;
	}

	public String getPrimary() {
		return primary;
	}

	public void setPrimary(String primary) {
		this.primary = primary;
	}
}
```

Mein Problem ist, dass XStream nicht damit zurecht kommt, dass ich in den Courses keine Tags mehr angebe. Das Feld "primary" in dem Course-Objekt soll ein Attribute des Course-Tags sein, was auch wunderbar funktioniert. Nur möchte XStream gerne, dass ich im Tag course noch einen weiteren Tag courseName anlege, gemäß der Objekt-Struktur. Kann ich dies irgendwie umgehen?

Das möchte XStream haben:
[XML]
...
	<courses>
		<course primary="true">
			<courseName>Vorspeisen</courseName>
		</course>
		<course>
			<courseName>Zwischengänge</courseName>
		</course>
		<course>
			<courseName>Desserts</courseName>
		</course>
	</courses>
...
[/XML]

Ich habe es bereits mit einem CustomConverter versucht. Mit diesem bekomme ich das Serialisieren soweit hin, dass ich tatsächlich keinen inneren "courseName" Tag mehr habe, nur kann ich dem übergeordneten Tag dann kein Attribut mehr geben und außerdem kann ich mit dieser Lösung nicht deserialisieren. Dabei bekomme ich eine NullPointer...

Der CustomConverter:

```
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;

import de.sevenload.domainObjects.Course;

public class CourseConverter implements Converter {

        public boolean canConvert(Class clazz) {
                return clazz.equals(Course.class);
        }

        public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) {
        	Course course = (Course) value;
	        // writer.startNode("courseName"); // by commenting this I removed the inner Tag
	        writer.setValue(course.getCourseName());
	        // writer.endNode(); // by commenting this I removed the inner Tag
	}
		
	public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
		Course course = new Course();
	        reader.moveDown();
	        course.setCourseName(reader.getValue());
	        reader.moveUp();
	        return course;
	}
}
```

Eigentlich ist XStream in der Handhabung deutlich einfacher als Simple, nur diese eine Hürde sorgt dafür, dass ich wohl XStream verwerfen muss und auf Simple bleibe, es sei denn, hier kann mir jemand helfen, der dieses Problem vielleicht schon einmal gelöst hat.

Ich hoffe, ich war ausführlich genug, dass sich jemand findet, der mir hlefen kann.
Schon einmal danke für die Mühe des Lesens und hoffentlich auch beim Beantworten!


----------



## Guybrush Threepwood (19. Jul 2012)

Hast Du es schon einmal mit transient versucht, also:

```
private transient String courseName;
```


----------



## luventas (20. Jul 2012)

Danke für deine Antwort, aber leider kann ich mit Transient nichts anfangen. Ich möchte ja den Inhalt des Tags ausgeben/einlesen, nur den Tag selber nicht. Transient sorgt ja dafür das dieses Feld ignoriert wird bei der Serialisierung / Deserialisierung.

ich möchte
[XML]
...
<courses>
  <course primary="true">Vorspeisen</course>
</courses>
...
[/XML]
auf

```
class Course {
  private String courseName;
  private String primary;
...
```
mappen. Das Feld courseName sol der Inhalt des course-TAGs sein. XStream benötigt hierfür aber
[XML]
...
<courses>
  <course primary="true">
    <courseName>Vorspeisen</courseName>
  </course>
</courses>
...
[/XML]
Ich möchte also nur den Tag "<courseName>" ausblenden können. Ist dies mit XStream möglich?

Danke sehr!


----------



## luventas (30. Jul 2012)

I found the solution by myself.
I had to implement a converter where I ask, if the attribute is set or not and read the value from the node directly.

[Java]
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;

import de.company.domainObjects.Course;

public class CourseConverter implements Converter {

	public CourseConverter() {
	}

	@SuppressWarnings("rawtypes")
	public boolean canConvert(final Class clazz) {
		return clazz.equals(Course.class);
	}

	public void marshal(final Object value, final HierarchicalStreamWriter writer, final MarshallingContext context) {
		final Course course = (Course) value;
		if (course.getPrimary() != null) {
			writer.addAttribute("primary", course.getPrimary());
		}
		writer.setValue(course.getCourse());
	}

	public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
		Course course = new Course();
		if (reader.getAttribute("primary") != null) {
			course.setPrimary(reader.getAttribute("primary"));
		}
		course.setCourse(reader.getValue());
		return course;
	}
}
[/code]

Thanks to all for thinking about a solution.


----------

