Rouven: Polymorphismus simulieren in XML vs. Java

Beitrag lesen

Hello,

Hallo zusammen,

ich habe ein kniffliges Problem aus dem Umfeld Polymorphismus mit XML und Polymorphismus mit Java. Folgendes ist das gewünschte Szenario:

  
<xsd:complexType name="super">  
	<xsd:sequence>  
		<xsd:element name="a" type="xsd:string"/>  
	</xsd:sequence>  
</xsd:complexType>  
<xsd:complexType name="sub">  
	<xsd:complexContent>  
		<xsd:extension base="super">  
			<xsd:sequence>  
				<xsd:element name="b" type="xsd:string"/>  
			</xsd:sequence>  
		</xsd:extension>  
	</xsd:complexContent>  
</xsd:complexType>  
<xsd:element name="MyElement" type="super" />  

Polymorphismus erlaubt mir folgende, valide Konstrukte:

  
<MyElement type="super">  
	<a>String</a>  
</MyElement>  
<MyElement type="sub">  
	<a>String</a>  
	<b>Another String</b>  
</MyElement>  

Nun habe ich leider ein Produkt vor mir, das von außen per XML angesprochen wird, dieses gegen ein XML-Schema validiert, aber dessen interner XML-Deserializer leider aus den Zeiten von DTDs stammt. Die Auswirkung: Er geht - zurecht - davon aus, dass ein Element mit Namen "A" auch immer einen Typ "A" hat. Obiges Beispiel ist also nicht umsetzbar, weil ich keine Klasse "MyElement" besitze.
Der Ausweg besteht im guten "alten" xsd:choice, mit dem ich - auf XML-Seite - praktisch gleichwertiges (ohne das MyElement) ausdrücken kann:

  
<xsd:choice>  
	<xsd:element ref="super" />  
	<xsd:element ref="sub" />	  
</xsd:choice>  

Nun habe ich aber mein Problem nur etwas verlagert, denn intern, d.h. hinter dem Deserializer, lauert eine Java-Klasse:

  
public class MyContainer {  
	private Super sup;  
}  

Nun bin ich etwas aufgeschmissen bei dem Versuch das Feld "sup" mit dem XML zu verheiraten. Denn hier setzt sich der DTD-basierte Ansatz des Produkts umgekehrt fort: "hmh, du bist vom Typ "A", dann gibts bestimmt ein void setA(A anA)."  Bisher ist mir nichts besseres eingefallen als:

  
public class MyContainer {  
	private Super sup;  
	  
	void setSub(Sub mySub) { sup = mySub; }  
	void setSuper(Super mySuper) { sub = mySuper; }  
}  

Die getter lasse ich mal außen vor, da ist der Mechanismus tatsächlich etwas ausgereifert. Soweit so gut, das hier wäre immer noch eine Lösung, generiere ich mir halt pro Subklasse einen Setter und mappe alles auf das selbe Feld.

Leider leider reicht das nicht, beide Probleme treffen sich noch in der Mitte:

  
<xsd:element name="SomeRelationship">  
	<xsd:complexType>  
		<xsd:sequence>  
			<xsd:element name="from" minOccurs="0" type="super" />  
			<xsd:element name="to" minOccurs="0" type="super" />  
		</xsd:sequence>  
	</xsd:complexType>  
</xsd:element>  

Durch die Hierarchie wird daraus:

  
<xsd:element name="SomeRelationship">  
	<xsd:complexType>  
		<xsd:sequence>  
			<xsd:choice minOccurs="0">  
				<xsd:element ref="super" />  
				<xsd:element ref="sub" />	  
			</xsd:choice>  
			<xsd:choice minOccurs="0">  
				<xsd:element ref="super" />  
				<xsd:element ref="sub" />	  
			</xsd:choice>  
		</xsd:sequence>  
	</xsd:complexType>  
</xsd:element>  

Doch halt - jetzt habe ich ein Element, das zweimal das selbe Unterelement enthält, nicht mehr vom Namen getrennt.
Nun bin ich zunächst mal tot. Die einzige Möglichkeit, aus dieser Nummer herauszukommen (mal Richtung DTD schielend), ist wenn ich mir leere Subtypen definiere, durch die ich das Aliasing durchführe:

  
<xsd:element name="SomeRelationship">  
	<xsd:complexType>  
		<xsd:sequence>  
			<xsd:choice minOccurs="0">  
				<xsd:element ref="superFrom />  
				<xsd:element ref="subFrom" />	  
			</xsd:choice>  
			<xsd:choice minOccurs="0">  
				<xsd:element ref="superTo" />  
				<xsd:element ref="subTo" />	  
			</xsd:choice>  
		</xsd:sequence>  
	</xsd:complexType>  
</xsd:element>  

Neben einer Explosion der Anzahl der Klassen in meinem Modell wird auch mein Java-Code nicht einfacher:

  
public class SomeRelationship {  
	private Super from;  
	private Super to;	  
	  
	/* ich kann Polymorphismus! */  
	public void setFrom(Super from) { this.from = from; }  
	public void setTo(Super to) { this.to = to; }  
	public Super getFrom() { return from; }  
	public Super getTo() { return to; }  
	  
	/* Mein Request-Parser leider nicht, der kann nur Typ=Name=Setter */  
	void setSubTo(SubTo mySub) { to = mySub; }  
	void setSuperTo(SuperTo mySuper) { to = mySuper; }  
	void setSubFrom(SubFrom mySub) { from = mySub; }  
	void setSuperFrom(SuperFrom mySuper) { from = mySuper; }  
}  

Das kann's doch nicht sein, oder? Ich hab hier Polymorphismus mit 10 Klassen, da werd ich ja wahnsinnig.

Any ideas? Ach ja, NEIN, es ist keine Option das Produkt auszutauschen. Es ist ebenfalls kaum eine Option dem Request Parser Polymorphismus im XML beizubringen. Es WÄRE möglich, statt die Subklassen für die Typhierarchie zu bilden die Umbenennung über einen zusätzlichen Container zu machen:

  
<xsd:element name="SomeRelationshipFrom">  
	<xsd:complexType>  
		<xsd:sequence>  
			<xsd:choice>  
				<xsd:element ref="super" />  
				<xsd:element ref="sub" />	  
			</xsd:choice>  
		</xsd:sequence>  
	</xsd:complexType>  
</xsd:element>  
<xsd:element name="SomeRelationshipTo">  
	<xsd:complexType>  
		<xsd:sequence>  
			<xsd:choice>  
				<xsd:element ref="super" />  
				<xsd:element ref="sub" />	  
			</xsd:choice>  
		</xsd:sequence>  
	</xsd:complexType>  
</xsd:element>  
<xsd:element name="SomeRelationship">  
        <xsd:complexType>  
                <xsd:sequence>  
			<xsd:element ref="SomeRelationshipFrom" />  
			<xsd:element ref="SomeRelationshipTo" />  
                </xsd:sequence>  
        </xsd:complexType>  
</xsd:element>  

...womit meine Java-Struktur nach der Deserialisierung allerdings ebenfalls eine Indirektion enthält:

  
   someRelationship.getSomeRelationshipFrom().getSuper();  

Thanks for any input!

MfG
Rouven

--
-------------------
sh:| fo:} ch:? rl:( br:& n4:{ ie:| mo:} va:) js:| de:] zu:| fl:( ss:) ls:& (SelfCode)
Because good guys need a break every once in a while.  --  Morty in "Click" (Columbia Pictures, 2006)