ResultSetをXMLファイルに変換しようとしています。私は最初にこの例をシリアライゼーションに使用しました。
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.Document;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSSerializer;
...
DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
DOMImplementationLS impl =
(DOMImplementationLS)registry.getDOMImplementation("LS");
...
LSSerializer writer = impl.createLSSerializer();
String str = writer.writeToString(document);
この作業を行った後、XMLファイルを検証しようとしましたが、いくつか警告がありました。 Doctypeがないことについての1つ。だから私はこれを実装する別の方法を試しました。 Transformerクラスに出会いました。このクラスを使用すると、エンコーディングやdoctypeなどを設定できます。
以前の実装では、自動名前空間修正がサポートされています。以下はそうではありません。
private static Document toDocument(ResultSet rs) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.newDocument();
URL namespaceURL = new URL("http://www.w3.org/2001/XMLSchema-instance");
String namespace = "xmlns:xsi="+namespaceURL.toString();
Element messages = doc.createElementNS(namespace, "messages");
doc.appendChild(messages);
ResultSetMetaData rsmd = rs.getMetaData();
int colCount = rsmd.getColumnCount();
String attributeValue = "true";
String attribute = "xsi:nil";
rs.beforeFirst();
while(rs.next()) {
amountOfRecords = 0;
Element message = doc.createElement("message");
messages.appendChild(message);
for(int i = 1; i <= colCount; i++) {
Object value = rs.getObject(i);
String columnName = rsmd.getColumnName(i);
Element messageNode = doc.createElement(columnName);
if(value != null) {
messageNode.appendChild(doc.createTextNode(value.toString()));
} else {
messageNode.setAttribute(attribute, attributeValue);
}
message.appendChild(messageNode);
}
amountOfRecords++;
}
logger.info("Amount of records archived: " + amountOfRecords);
TransformerFactory tff = TransformerFactory.newInstance();
Transformer tf = tff.newTransformer();
tf.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
tf.setOutputProperty(OutputKeys.INDENT, "yes");
BufferedWriter bf = createFile();
StreamResult sr = new StreamResult(bf);
DOMSource source = new DOMSource(doc);
tf.transform(source, sr);
return doc;
}
以前の実装をテストしているときに、TransformationExceptionが発生しました。プレフィックス 'xsi'の名前空間が宣言されていません。ご覧のとおり、ドキュメントのルート要素にxsiプレフィックス付きの名前空間を追加しようとしました。これをテストした後も、例外が発生しました。名前空間とそのプレフィックスを設定する正しい方法は何ですか?
編集:最初の実装でもう1つの問題は、XMLドキュメントの最後の要素に最後の3つの終了タグがないことです。
ルートノードに名前空間宣言を追加していません。名前空間でルートノードを宣言しただけで、2つのまったく異なるものです。 DOMを構築するときは、関連するすべてのノードで名前空間を参照する必要があります。つまり、属性を追加するときは、その名前空間(たとえば、setAttributeNS)を定義する必要があります。
補足:XML名前空間はURLのように見えますが、実際はそうではありません。ここではURLクラスを使用する必要はありません。
NamespaceAwareドキュメントにノードを設定する正しい方法は、以下を使用することです。
rootNode.createElementNS("http://example/namespace", "PREFIX:aNodeName");
したがって、「PREFIX」を独自のカスタムプレフィックスに置き換え、「aNodeName」をノードの名前に置き換えることができます。各ノードが独自の名前空間宣言を持たないようにするには、次のようにルートノードの属性として名前空間を定義できます。
rootNode.setAttribute("xmlns:PREFIX", "http://example/namespace");
必ず設定してください:
documentBuilderFactory.setNamespaceAware(true)
それ以外の場合は、namespaceAwarenessがありません。
SetAttributeでxmlns-prefixを設定するのは間違っていることに注意してください。たとえばDOMに署名したい場合は、setAttributeNSを使用する必要があります:element.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:PREFIX", "http://example/namespace");