Ich versuche, mich in ein System eines Drittanbieters zu integrieren. Je nach Objekttyp ändert sich das Stammelement des zurückgegebenen XML-Dokuments. Ich verwende die JAXB-Bibliothek für Marshalling / Unmarshalling.
Root1:
<?xml version="1.0" encoding="UTF-8"?>
<root1 id='1'>
<MOBILE>9831138683</MOBILE>
<A>1</A>
<B>2</B>
</root1>
Root2:
<?xml version="1.0" encoding="UTF-8"?>
<root2 id='3'>
<MOBILE>9831138683</MOBILE>
<specific-attr1>1</specific-attr1>
<specific-attr2>2</specific-attr2>
</root2>
Ich verwende all die verschiedenen XML-Dateien, die sie einem generischen Objekt zuordnen :
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "ROW")
public class Row {
@XmlAttribute
private int id;
@XmlElement(name = "MOBILE")
private int mobileNo;
@XmlMixed
@XmlAnyElement
@XmlJavaTypeAdapter(MyMapAdapter.class)
private Map<String, String> otherElements;
}
Und der Adapter zum Verwandeln der unbekannten Werte in eine Karte :
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.parsers.DocumentBuilderFactory;
import java.util.HashMap;
import java.util.Map;
public class MyMapAdapter extends XmlAdapter<Element, Map<String, String>> {
private Map<String, String> hashMap = new HashMap<>();
@Override
public Element marshal(Map<String, String> map) throws Exception {
// expensive, but keeps the example simpler
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Element root = document.createElement("dynamic-elements");
for(Map.Entry<String, String> entry : map.entrySet()) {
Element element = document.createElement(entry.getKey());
element.setTextContent(entry.getValue());
root.appendChild(element);
}
return root;
}
@Override
public Map<String, String> unmarshal(Element element) {
String tagName = element.getTagName();
String elementValue = element.getChildNodes().item(0).getNodeValue();
hashMap.put(tagName, elementValue);
return hashMap;
}
}
Dadurch werden ID und Handynummer in die Felder und der Rest, das Unbekannte, in eine Karte eingefügt .
Dies funktioniert, wenn das Stammelement wie im obigen Beispiel auf ROW festgelegt ist.
Wie kann dies so funktionieren, dass das Stammelement in jedem XML unterschiedlich ist? Ein Weg, um beim Unmarshalling vielleicht nur agnostisch gegenüber Root-Elementen zu sein?
Antworten:
Vergessen Sie dafür JAXB und analysieren Sie es selbst mit StAX.
Im folgenden Code habe ich das Feld
mobileNo
vonint
in geändertString
, da der Wert9831138683
für ein zu groß istint
.Prüfung
Ausgabe
quelle
xml
es besser wäre, auf einer niedrigeren Abstraktionsebene (über StAX) für diese Art von vs Unmarhshalling über JAXB (für bekannte Datenobjekte, in denen die XSD definiert ist) zu arbeiten. Selbst wenn ich StAX verwende, muss ich dasRow
Objekt mit dem passenden Stammelement wieder in die richtige XML- Datei serialisieren .Row
Klasse den Namen merken. --- Wenn Sie sich um die JSON-Eingabe kümmern müssen, verwenden Sie einen JSON-Parser.mobileNo
eines Strings . Selbst wennint
es groß genug war, um eine Telefonnummer zu speichern, repräsentiert der Wert keinen Betrag. IMO, dies garantiert, dass es als Zeichenfolge behandelt wird, auch wenn der Wert numerisch ist.Ich glaube nicht, dass es einen Weg gibt, das zu tun, was Sie verlangen. In XML muss der Stammknoten (das Dokument) ein definiertes Element (oder eine definierte Klasse) haben. Mit anderen Worten
xs:any
, funktioniert nur für Unterelemente. Selbst wenn es einen Weg gab, dies zu erreichen, ist dies meiner Meinung nach eine schlechte Entscheidung. Anstatt ein variables ("dynamisches") Stammelement zu erstellen, sollten Sie demselben Element ein Namensattribut hinzufügen, um die XML-Dateien zu unterscheiden. Zum Beispiel:Dazu müssen Sie Ihrem vorhandenen Element lediglich ein Namensattribut hinzufügen:
quelle
XmlRootElement
vonRow
und unmarshalling zuJAXBElement
durch:JAXBElement<Row> element = unmarshaller.unmarshal(new StreamSource(xml), Row.class); Row root = element.getValue();
. Dies ermöglicht das Auffüllen aller Felder und das Konsumieren von XMLs mit unterschiedlichen Stammelementen.xs:any
. Ich habe dies auch mit dem XML-Schema versucht. Wenn Sie Ihr Stammelement so einstellen, dass es im Grunde genommen alles ist, bricht Ihr Schema. Mit anderen Worten,xs:any
kann nur im Kontext eines untergeordneten Knotens verwendet werden. Zuletzt müssen Sie zu Ihrem Kunden gehen und ihm zeigen, warum dies eine schlechte Implementierung ist.XmlRootElement
vonRow
beim Aufmarschieren funktioniert. Meine Frage an Sie ist, wie würden Sie es aufstellen, wenn Sie müssen? Sie können dies mit anderen Mechanismen tun, jedoch nicht mit JAXB. Der Nachteil dieses Ansatzes besteht darin, dass Sie dann Ihre eigenen Validatoren schreiben müssen. Machbar, aber Sie verlieren die nativen Fähigkeiten von JAXB, um dies zu tun.