使用 SQL 结果创建 XML 文档
算法
创建新 XML 文档的过程如下:
1 解析映射文件,以便获得必要的信息,包括要检索的数据在内。
2 检索源查询。这允许根据映射文件动态检索数据。
3 将数据存储到文档对象中。随后我们将从这个临时文档中拉取数据,以便根据映射创建目标文档。
4 检索数据映射,使应用程序可以使用它。
5 循环遍历原始数据。分析每一行数据,并将其重新映射到新结构。
6 检索元素映射。映射文件定义了从临时文档中按照怎样的顺序拉取哪些数据。
7 为新文档添加元素。检索数据之后,将其添加到新文档的新名称下。
8 为新文档添加属性。最后,将任何属性添加到恰当的元素中。
解析映射文件
创建新文档的第一步是检索映射,这只能通过解析映射文件完成。请注意,由于此文件也包括对最终将检索的数据的引用,因此您必须先解析此文件,然后才能执行数据库操作。
... import org.w3c.dom.Document; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; public class Pricing extends Object { public static void main (String args[]){ //Create the Document object Document mapDoc = null; try { //Create the DocumentBuilderFactory DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance(); //Create the DocumentBuilder DocumentBuilder docbuilder = dbfactory.newDocumentBuilder(); //Parse the file to create the Document mapDoc = docbuilder.parse("mapping.xml"); } catch (Exception e) { System.out.println("Problem creating document: "+e.getMessage()); } //For the JDBC-ODBC bridge, use //driverName = "sun.jdbc.odbc.JdbcOdbcDriver" //and //connectURL = "jdbc:odbc:pricing" String driverName = "JData2_0.sql.$Driver"; String connectURL = "jdbc:JDataConnect://127.0.0.1/pricing"; Connection db = null; ... |
接下来检索 data 元素的 sql 属性中存储的源查询。
... import org.w3c.dom.Element; import org.w3c.dom.Node; ... System.out.println("Problem creating document: "+e.getMessage()); } //Retrieve the root element Element mapRoot = mapDoc.getDocumentElement(); //Retrieve the (only) data element and cast it to Element Node dataNode = mapRoot.getElementsByTagName("data").item(0); Element dataElement = (Element)dataNode; //Retrieve the sql statement String sql = dataElement.getAttribute("sql"); //Output the SQL statement System.out.println(sql); //For the JDBC-ODBC bridge, use //driverName = "sun.jdbc.odbc.JdbcOdbcDriver" //and //connectURL = "jdbc:odbc:pricing" String driverName = "JData2_0.sql.$Driver"; String connectURL = "jdbc:JDataConnect://127.0.0.1/pricing"; Connection db = null; ... |
首先确定根元素,随后检索 data 节点。由于仅有一个 data 元素,因此您可以直接检索。类似的技术也可以用于通过多个依序运行的查询构建文档。
最后,将 Node 强制转换为 Element,以便获得 Attribute 值。
删除之前的输出语句,运行应用程序,显示 SQL 语句作为输出。
图 9. SQL 语句作为输出
将数据存储到文档对象中
从数据库中成功提取数据之后,数据将存储在临时的 Document 之中。通用方法是为每个数据行创建一个 row 元素,将每列表示为根据该列命名的一个元素,将数据本身作为元素的内容。
... public static void main (String args[]){ Document mapDoc = null; //Define a new Document object Document dataDoc = null; try { //Create the DocumentBuilderFactory DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance(); //Create the DocumentBuilder DocumentBuilder docbuilder = dbfactory.newDocumentBuilder(); //Parse the file to create the Document mapDoc = docbuilder.parse("mapping.xml"); //Instantiate a new Document object dataDoc = docbuilder.newDocument(); } catch (Exception e) { System.out.println("Problem creating document: "+e.getMessage()); } ... ResultSetMetaData resultmetadata = null;
//Create a new element called "data" Element dataRoot = dataDoc.createElement("data"); try { statement = db.createStatement(); resultset = statement.executeQuery("select * from products"); resultmetadata = resultset.getMetaData(); int numCols = resultmetadata.getColumnCount(); while (resultset.next()) { //For each row of data //Create a new element called "row" Element rowEl = dataDoc.createElement("row"); for (int i=1; i <= numCols; i++) { //For each column, retrieve the name and data String colName = resultmetadata.getColumnName(i); String colVal = resultset.getString(i); //If there was no data, add "and up" if (resultset.wasNull()) { colVal = "and up"; } //Create a new element with the same name as the column Element dataEl = dataDoc.createElement(colName); //Add the data to the new element dataEl.appendChild(dataDoc.createTextNode(colVal)); //Add the new element to the row rowEl.appendChild(dataEl); } //Add the row to the root element dataRoot.appendChild(rowEl); } } catch (SQLException e) { System.out.println("SQL Error: "+e.getMessage()); } finally { System.out.println("Closing connections..."); try { db.close(); } catch (SQLException e) { System.out.println("Can't close connection."); } } //Add the root element to the document dataDoc.appendChild(dataRoot); } } |
具体来说,参见上面的代码示例,首先创建一个空文档以及根元素 data。对于数据库中的每一行,创建一个 row 元素,为每一列创建一个独立的元素,并将其添加到行中。最后,将各 row 元素添加到根,将根添加到 Document。
检索数据映射
获得数据之后,便可以开始将其映射到新结构:从解析后的映射文档中检索映射。先检索根元素和行元素中的信息,随后检索元素映射本身。
... import org.w3c.dom.NodeList; ... dataDoc.appendChild(dataRoot); //Retrieve the root element (also called "root") Element newRootInfo = (Element)mapRoot.getElementsByTagName("root").item(0); //Retrieve the root and row information String newRootName = newRootInfo.getAttribute("name"); String newRowName = newRootInfo.getAttribute("rowName"); //Retrieve information on elements to be built in the new document NodeList newNodesMap = mapRoot.getElementsByTagName("element"); } } |
有了这些信息之后,就可以构建新的 Document。
循环遍历原始数据
每个原始行均作为一个 row 元素存储在临时文件之中。您需要循环遍历这些元素,将其作为 NodeList 检索。
... NodeList newNodesMap = mapRoot.getElementsByTagName("element"); //Retrieve all rows in the old document NodeList oldRows = dataRoot.getElementsByTagName("row"); for (int i=0; i < oldRows.getLength(); i++){ //Retrieve each row in turn Element thisRow = (Element)oldRows.item(i); } ... |
检索元素映射
既然已经获得了数据和映射信息,下面就可以开始构建新的 Document。对于每一行,循环遍历映射,确定从临时 Document 中检索数据列的顺序,并确定在将其添加到新的 Document 中时应对其采用什么样的名称。
... for (int i=0; i < oldRows.getLength(); i++){ //Retrieve each row in turn Element thisRow = (Element)oldRows.item(i); for (int j=0; j < newNodesMap.getLength(); j++) { //For each node in the new mapping, retrieve the information //First the new information... Element thisElement = (Element)newNodesMap.item(j); String newElementName = thisElement.getAttribute("name"); //Then the old information Element oldElement = (Element)thisElement.getElementsByTagName("content").item(0); String oldField = oldElement.getFirstChild().getNodeValue(); } } ... |
对于 newNodesMap 中的每一个元素,应用程序都会检索新元素名称,随后会检索要检索的旧元素的名称。
向新文档添加元素
将新元素添加到文档之中非常简单,只需使用恰当的名称创建新元素,随后检索恰当的数据并将其设置为元素的内容即可。
... public static void main (String args[]){ Document mapDoc = null; Document dataDoc = null; //Create the new Document Document newDoc = null; try { //Create the DocumentBuilderFactory DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance(); //Create the DocumentBuilder DocumentBuilder docbuilder = dbfactory.newDocumentBuilder(); //Parse the file to create the Document mapDoc = docbuilder.parse("mapping.xml"); //Instantiate a new Document object dataDoc = docbuilder.newDocument(); //Instantiate the new Document newDoc = docbuilder.newDocument(); } catch (Exception e) { System.out.println("Problem creating document: "+e.getMessage()); } ... //Retrieve the root element (also called "root") Element newRootInfo = (Element)mapRoot.getElementsByTagName("root").item(0); //Retrieve the root and row information String newRootName = newRootInfo.getAttribute("name"); String newRowName = newRootInfo.getAttribute("rowName"); //Retrieve information on elements to be built in the new document NodeList newNodesMap = mapRoot.getElementsByTagName("element"); //Create the final root element with the name from the mapping file Element newRootElement = newDoc.createElement(newRootName); NodeList oldRows = dataRoot.getElementsByTagName("row"); for (int i=0; i < oldRows.getLength(); i++){ //For each of the original rows Element thisRow = (Element)oldRows.item(i); //Create the new row Element newRow = newDoc.createElement(newRowName);
for (int j=0; j < newNodesMap.getLength(); j++) {
//Get the mapping information for each column Element thisElement = (Element)newNodesMap.item(j); String newElementName = thisElement.getAttribute("name"); Element oldElement = (Element)thisElement.getElementsByTagName("content").item(0); String oldField = oldElement.getFirstChild().getNodeValue(); //Get the original values based on the mapping information Element oldValueElement = (Element)thisRow.getElementsByTagName(oldField).item(0); String oldValue = oldValueElement.getFirstChild().getNodeValue(); //Create the new element Element newElement = newDoc.createElement(newElementName); newElement.appendChild(newDoc.createTextNode(oldValue)); //Add the new element to the new row newRow.appendChild(newElement); } //Add the new row to the root newRootElement.appendChild(newRow); } //Add the new root to the document newDoc.appendChild(newRootElement); } } |
首先,需要创建新的 Document,随后创建新的根元素,其名称会获取自映射信息。对于临时 Document 中的每一行,可以使用映射中指定的 newRowName 为新行创建一个元素。
对于每一行,循环遍历映射中指定的各新元素,并检索原始数据。在上一节中,您已经按顺序检索了 content 元素的文本。现在您可以使用此信息来确定将按照什么样的顺序从临时行中检索哪些节点。获得了旧数据和新名称之后,可以创建新元素并将其添加到行中。
最后,将新行添加到根元素,将根元素添加到 Document。最后要添加的内容就是属性。
为新文档添加属性
新 Document 已经接近完成。您已经添加了新元素,但尚未添加可能指定的任何属性。按照与添加新元素本身相似的方式添加这些属性。然而,一个元素有可能具有多项属性,因此在代码中必须考虑到这个问题。完成这项任务的最简单的方法就是检索 attribute 元素,并将其置入 NodeList,随后遍历列表,处理各元素。对于每一个所需属性,通过原始 Document 确定字段名称,并确定其在新Document 中的名称。
... Element newElement = newDoc.createElement(newElementName); newElement.appendChild(newDoc.createTextNode(oldValue)); //Retrieve list of new elements NodeList newAttributes = thisElement.getElementsByTagName("attribute"); for (int k=0; k < newAttributes.getLength(); k++) { //For each new attribute //Get the mapping information Element thisAttribute = (Element)newAttributes.item(k); String oldAttributeField = thisAttribute.getFirstChild().getNodeValue(); String newAttributeName = thisAttribute.getAttribute("name"); //Get the original value oldValueElement = (Element)thisRow.getElementsByTagName(oldAttributeField).item(0); String oldAttributeValue = oldValueElement.getFirstChild().getNodeValue();
//Create the new attribute newElement.setAttribute(newAttributeName, oldAttributeValue); } //Add the element to the new row newRow.appendChild(newElement); } //Add the new row to the root newRootElement.appendChild(newRow); ... |
最终文档
这个过程结束时,newDoc 包含采用新格式的旧信息。之后即可在另一个应用程序中使用它,或者使用 XSLT 或其他方法实现进一步的转换。
<pricingInfo> <product> <description product_number="1">Filet Mignon</description> <quantity>1</quantity> <size>1</size> <sizeUnit>item</sizeUnit> <quantityPrice>40</quantityPrice> </product> <product> <description product_number="2">Filet Mignon</description> <quantity>11</quantity> <size>1</size> <sizeUnit>item</sizeUnit> <quantityPrice>30</quantityPrice> </product> <product> <description product_number="3">Filet Mignon</description> <quantity>101</quantity> <size>1</size> <sizeUnit>item</sizeUnit> <quantityPrice>20</quantityPrice> </product> <product> <description product_number="4">Prime Rib</description> <quantity>1</quantity> <size>1</size> <sizeUnit>lb</sizeUnit> <quantityPrice>20</quantityPrice> </product> <product> <description product_number="5">Prime Rib</description> <quantity>101</quantity> <size>1</size> <sizeUnit>lb</sizeUnit> <quantityPrice>15</quantityPrice> </product> ...
|