java - 需要对xml数据执行sql查询

标签 java sql xml xml-parsing

目前我们有几个查询,我们从数据库获取数据并将其存储在内存中。现在,对于相同的查询,我们需要一种从 xml 而不是数据库服务器获取数据的方法,即数据将以以下形式保存在 xml 中:

    <?xml version='1.0'  encoding='Cp1252' ?>
    <tables>
    <TableOne>
    <Row1>
    <Column1>TableOne Row1 Column1 Value</Column1>
    <Column2>TableOne Row1 Column2 Value</Column2>
    <Column3>TableOne Row1 Column3 Value</Column3>
    </Row1>
    <Row2>
    <Column1>TableOne Row2 Column1 Value</Column1>
    <Column2>TableOne Row2 Column2 Value</Column2>
    <Column3>TableOne Row2 Column3 Value</Column3>
    </Row2>
    </TableOne>
    <TableTwo>
    <TableTwoRow1>
    <TableTwoRow1Column1>TableOne Row1 Column1 Value</TableTwoRow1Column1>
    <TableTwoRow1Column2>TableTwoRow1 Column2 Value</TableTwoRow1Column2>
    <TableTwoRow1Column3>TableTwoRow1 Column3 Value</TableTwoRow1Column3>
    </TableTwoRow1>
    <TableTwoRow2>
    <TableTwoRow2Column1>TableTwoRow2 Column1 Value</TableTwoRow2Column1>
    <TableTwoRow2Column2>TableTwoRow2 Column2 Value</TableTwoRow2Column2>
    <TableTwoRow2Column3>TableTwoRow2 Column3 Value</TableTwoRow2Column3>
    </TableTwoRow2>
    </TableTwo>
    </tables>

为了实现上述功能,我制作了自己的数据结构来存储上述 xml:

    public class DataObject {

    Map<String, Table> tables = new HashMap<String, Table>();

    public Map<String, Table> getTables() {
    return tables;
}

public void setTable(Map<String, Table> table) {
    this.tables = table;
}

    }

然后表格将如下所示:

    public class Table {

private String tableName;
private List<Map<String,String>> rowList = new ArrayList<Map<String,String>>();

public Table(String tableName) {
    super();
    this.tableName = tableName;
}

public List<Map<String, String>> getRowList() {
    return rowList;
}

public void setRowList(List<Map<String, String>> rowList) {
    this.rowList = rowList;
}

public String getTableName() {
    return tableName;
}

public void setTableName(String tableName) {
    this.tableName = tableName;
}   

}

每一行将表示为 Map,多行将表示为 List。我已经能够将数据从 xml 传递到上面的“DataObject”,如下所示:

私有(private)DataObject getDataObject(文档文档){

    DataObject dataObject = new DataObject();

    Element rootElement = document.getRootElement();
    List<Element> tableList = rootElement.getChildren();

    for (Element tableElement : tableList) {

        String tableName = tableElement.getName();
        Table table = new Table(tableName);
        dataObject.getTables().put(tableName, table);
        List<Element> rows = tableElement.getChildren();

        for (Element rowElement : rows) {

            Map<String, String> row = new HashMap<String, String>();

            List<Element> columns = rowElement.getChildren();
            for (Element columnElement : columns) {
                String columnName = columnElement.getName();
                String columnValue = columnElement.getValue();
                row.put(columnName, columnValue);
            }

            table.getRowList().add(row);
        }
    }

    return dataObject;

}

现在我想不出一种有效的方法来查询上述 dataObject 来执行查询,而且我找不到任何内置 API 可以帮助我做到这一点。查询将具有 select、group by、order by 和所有逻辑运算符。 任何想法都将不胜感激。

最佳答案

为什么是 SQL?考虑另一种专门为 XML 设计的专用声明性语言:XSLT 。 Java 附带了 XSLT 1.0 处理器以及 XSLT 2.0 和 3.0 的外部处理器,包括 Xalan and Saxon在Java中运行。在 XSLT 中,您可以找到大多数 SQL 对应项,因为文档可以根据 XPath 表达式、模板处理以及基本逻辑和算术运算进行转换。请注意,XSLT 脚本是格式正确的 XML 文件(带有特殊说明)!

下面是一个使用截至 2015 年 8 月一周前 20 位 Stackoverflow 用户的示例。示例显示了 XSLT 中的对应 SQL。在此示例中,topusers 是表,其子项是列。我们的好 friend @JB Nizet 上面的评论已包含在 XML 中!

XML来源

<?xml version='1.0' encoding='UTF-8'?>
<stackoverflow>
  <topusers>
    <user>Gordon Linoff</user>
    <link>https://stackoverflow.com/users/1144035/gordon-linoff</link>
    <goldbadges>18</goldbadges>
    <silverbadges>81</silverbadges>
    <bronzebadges>134</bronzebadges>
    <membership>3 years, 6 months</membership>
    <yearRank>#1</yearRank>
    <totalReputation>320356</totalReputation>
    <yearReputation>72008</yearReputation>
    <topskill>sql</topskill>
  </topusers>
  <topusers>
    <user>Martijn Pieters</user>
    <link>https://stackoverflow.com/users/100297/martijn-pieters</link>
    <goldbadges>36</goldbadges>
    <silverbadges>787</silverbadges>
    <bronzebadges>975</bronzebadges>
    <membership>6 years, 3 months</membership>
    <yearRank>#2</yearRank>
    <totalReputation>385697</totalReputation>
    <yearReputation>66886</yearReputation>
    <topskill>python</topskill>
  </topusers>
  <topusers>
    <user>anubhava</user>
    <link>https://stackoverflow.com/users/548225/anubhava</link>
    <goldbadges>25</goldbadges>
    <silverbadges>100</silverbadges>
    <bronzebadges>173</bronzebadges>
    <membership>4 years, 7 months</membership>
    <yearRank>#3</yearRank>
    <totalReputation>279930</totalReputation>
    <yearReputation>62759</yearReputation>
    <topskill>regex</topskill>
  </topusers>
  <topusers>
    <user>Jon Skeet</user>
    <link>https://stackoverflow.com/users/22656/jon-skeet</link>
    <goldbadges>379</goldbadges>
    <silverbadges>5537</silverbadges>
    <bronzebadges>6720</bronzebadges>
    <membership>6 years, 10 months</membership>
    <yearRank>#4</yearRank>
    <totalReputation>797069</totalReputation>
    <yearReputation>57963</yearReputation>
    <topskill>c#</topskill>
  </topusers>
  <topusers>
    <user>akrun</user>
    <link>https://stackoverflow.com/users/3732271/akrun</link>
    <goldbadges>5</goldbadges>
    <silverbadges>25</silverbadges>
    <bronzebadges>58</bronzebadges>
    <membership>1 year, 1 month</membership>
    <yearRank>#5</yearRank>
    <totalReputation>88928</totalReputation>
    <yearReputation>57384</yearReputation>
    <topskill>r</topskill>
  </topusers>
  <topusers>
    <user>VonC</user>
    <link>https://stackoverflow.com/users/6309/vonc</link>
    <goldbadges>131</goldbadges>
    <silverbadges>1354</silverbadges>
    <bronzebadges>1447</bronzebadges>
    <membership>6 years, 10 months</membership>
    <yearRank>#6</yearRank>
    <totalReputation>512007</totalReputation>
    <yearReputation>56260</yearReputation>
    <topskill>git</topskill>
  </topusers>
  <topusers>
    <user>CommonsWare</user>
    <link>https://stackoverflow.com/users/115145/commonsware</link>
    <goldbadges>53</goldbadges>
    <silverbadges>1104</silverbadges>
    <bronzebadges>1155</bronzebadges>
    <membership>6 years, 2 months</membership>
    <yearRank>#7</yearRank>
    <totalReputation>487584</totalReputation>
    <yearReputation>54921</yearReputation>
    <topskill>android</topskill>
  </topusers>
  <topusers>
    <user>T.J. Crowder</user>
    <link>https://stackoverflow.com/users/157247/t-j-crowder</link>
    <goldbadges>51</goldbadges>
    <silverbadges>556</silverbadges>
    <bronzebadges>692</bronzebadges>
    <membership>5 years, 11 months</membership>
    <yearRank>#8</yearRank>
    <totalReputation>374425</totalReputation>
    <yearReputation>54121</yearReputation>
    <topskill>javascript</topskill>
  </topusers>
  <topusers>
    <user>Hans Passant</user>
    <link>https://stackoverflow.com/users/17034/hans-passant</link>
    <goldbadges>62</goldbadges>
    <silverbadges>679</silverbadges>
    <bronzebadges>1277</bronzebadges>
    <membership>6 years, 10 months</membership>
    <yearRank>#9</yearRank>
    <totalReputation>565002</totalReputation>
    <yearReputation>53886</yearReputation>
    <topskill>c#</topskill>
  </topusers>
  <topusers>
    <user>BalusC</user>
    <link>https://stackoverflow.com/users/157882/balusc</link>
    <goldbadges>148</goldbadges>
    <silverbadges>1925</silverbadges>
    <bronzebadges>2235</bronzebadges>
    <membership>5 years, 11 months</membership>
    <yearRank>#10</yearRank>
    <totalReputation>584857</totalReputation>
    <yearReputation>53693</yearReputation>
    <topskill>java</topskill>
  </topusers>
  <topusers>
    <user>dasblinkenlight</user>
    <link>https://stackoverflow.com/users/335858/dasblinkenlight</link>
    <goldbadges>31</goldbadges>
    <silverbadges>342</silverbadges>
    <bronzebadges>617</bronzebadges>
    <membership>5 years, 3 months</membership>
    <yearRank>#11</yearRank>
    <totalReputation>354707</totalReputation>
    <yearReputation>52779</yearReputation>
    <topskill>java</topskill>
  </topusers>
  <topusers>
    <user>Eran</user>
    <link>https://stackoverflow.com/users/1221571/eran</link>
    <goldbadges>18</goldbadges>
    <silverbadges>114</silverbadges>
    <bronzebadges>187</bronzebadges>
    <membership>3 years, 5 months</membership>
    <yearRank>#12</yearRank>
    <totalReputation>105090</totalReputation>
    <yearReputation>49529</yearReputation>
    <topskill>java</topskill>
  </topusers>
  <topusers>
    <user>Avinash Raj</user>
    <link>https://stackoverflow.com/users/3297613/avinash-raj</link>
    <goldbadges>6</goldbadges>
    <silverbadges>25</silverbadges>
    <bronzebadges>59</bronzebadges>
    <membership>1 year, 5 months</membership>
    <yearRank>#13</yearRank>
    <totalReputation>96627</totalReputation>
    <yearReputation>48928</yearReputation>
    <topskill>regex</topskill>
  </topusers>
  <topusers>
    <user>Arun P Johny</user>
    <link>https://stackoverflow.com/users/114251/arun-p-johny</link>
    <goldbadges>30</goldbadges>
    <silverbadges>194</silverbadges>
    <bronzebadges>254</bronzebadges>
    <membership>6 years, 2 months</membership>
    <yearRank>#14</yearRank>
    <totalReputation>218919</totalReputation>
    <yearReputation>45858</yearReputation>
    <topskill>jquery</topskill>
  </topusers>
  <topusers>
    <user>Alex Martelli</user>
    <link>https://stackoverflow.com/users/95810/alex-martelli</link>
    <goldbadges>64</goldbadges>
    <silverbadges>752</silverbadges>
    <bronzebadges>1056</bronzebadges>
    <membership>6 years, 3 months</membership>
    <yearRank>#15</yearRank>
    <totalReputation>402886</totalReputation>
    <yearReputation>44772</yearReputation>
    <topskill>python</topskill>
  </topusers>
  <topusers>
    <user>unutbu</user>
    <link>https://stackoverflow.com/users/190597/unutbu</link>
    <goldbadges>28</goldbadges>
    <silverbadges>473</silverbadges>
    <bronzebadges>617</bronzebadges>
    <membership>5 years, 9 months</membership>
    <yearRank>#16</yearRank>
    <totalReputation>295951</totalReputation>
    <yearReputation>44389</yearReputation>
    <topskill>python</topskill>
  </topusers>
  <topusers>
    <user>JB Nizet</user>
    <link>https://stackoverflow.com/users/571407/jb-nizet</link>
    <goldbadges>22</goldbadges>
    <silverbadges>307</silverbadges>
    <bronzebadges>491</bronzebadges>
    <membership>4 years, 6 months</membership>
    <yearRank>#17</yearRank>
    <totalReputation>319659</totalReputation>
    <yearReputation>43697</yearReputation>
    <topskill>java</topskill>
  </topusers>
  <topusers>
    <user>Quentin</user>
    <link>https://stackoverflow.com/users/19068/quentin</link>
    <goldbadges>42</goldbadges>
    <silverbadges>491</silverbadges>
    <bronzebadges>664</bronzebadges>
    <membership>6 years, 10 months</membership>
    <yearRank>#18</yearRank>
    <totalReputation>397762</totalReputation>
    <yearReputation>43626</yearReputation>
    <topskill>javascript</topskill>
  </topusers>
  <topusers>
    <user>Darin Dimitrov</user>
    <link>https://stackoverflow.com/users/29407/darin-dimitrov</link>
    <goldbadges>108</goldbadges>
    <silverbadges>2089</silverbadges>
    <bronzebadges>2107</bronzebadges>
    <membership>6 years, 9 months</membership>
    <yearRank>#19</yearRank>
    <totalReputation>604319</totalReputation>
    <yearReputation>43621</yearReputation>
    <topskill>c#</topskill>
  </topusers>
  <topusers>
    <user>alecxe</user>
    <link>https://stackoverflow.com/users/771848/alecxe</link>
    <goldbadges>19</goldbadges>
    <silverbadges>108</silverbadges>
    <bronzebadges>194</bronzebadges>
    <membership>4 years, 2 months</membership>
    <yearRank>#20</yearRank>
    <totalReputation>116413</totalReputation>
    <yearReputation>42215</yearReputation>
    <topskill>python</topskill>
  </topusers>
</stackoverflow>

SELECT 语句

SELECT User, Membership, Link
FROM topusers
WHERE topskill = 'Java'
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>

  <xsl:template match="/stackoverflow">
    <xsl:copy>      
      <xsl:apply-templates select="topusers[topskill='java']"/>
    </xsl:copy>
  </xsl:template>  

  <xsl:template match="topusers">
    <xsl:copy>      
      <xsl:copy-of select="user"/>
      <xsl:copy-of select="membership"/>
      <xsl:copy-of select="link"/>
    </xsl:copy>
  </xsl:template>

</xsl:transform>

分组依据

SELECT topskill AS skill, SUM(goldbadges) AS SumOfGoldBadges,     
       SUM(silverbadges) AS SumOfSilverBadges, SUM(bronzebadges) AS SumOfBronzeBadges, 
       AVG(totalReputation) AS AvgOfReps,
       Min(yearReputation) As MinOfYrReps, Max(yearReputation) As MaxOfYrReps
FROM topUsers
GROUP BY topskill
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
               xmlns:exsl="http://exslt.org/common" extension-element-prefixes="exsl">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>

<xsl:key name="skillkey" match="topusers" use="topskill" />

  <xsl:template match="/stackoverflow">
    <xsl:copy>
      <xsl:apply-templates select="topusers[generate-id() = generate-id(key('skillkey', topskill)[1])]"/>
    </xsl:copy>
  </xsl:template>

  <!-- Muenchian Method -->
  <xsl:template match="topusers">
    <xsl:variable name="curr-group" select="key('skillkey', topskill)" />

    <skill_group>      
      <skill><xsl:value-of select="$curr-group/topskill"/></skill>
      <SumOfGoldBadges><xsl:value-of select="sum($curr-group/goldbadges)"/></SumOfGoldBadges>
      <SumOfSilverBadges><xsl:value-of select="sum($curr-group/silverbadges)"/></SumOfSilverBadges>
      <SumOfBronzeBadges><xsl:value-of select="sum($curr-group/bronzebadges)"/></SumOfBronzeBadges>
      <AvgOfReps><xsl:value-of select="sum($curr-group/totalReputation)
                                            div count($curr-group/totalReputation)"/></AvgOfReps>

         <xsl:variable name="repsSorted">
              <xsl:for-each select="$curr-group">
                  <xsl:sort select="yearReputation" data-type="number" order="ascending"/>
                  <xsl:copy-of select="yearReputation"/>
              </xsl:for-each>
          </xsl:variable>
          <xsl:variable name="repsSortedSet" select="exsl:node-set($repsSorted)/yearReputation" />

      <MinOfYrReps><xsl:value-of select="$repsSortedSet[1]"/></MinOfYrReps>
      <MaxOfYrReps><xsl:value-of select="$repsSortedSet[last()]"/></MaxOfYrReps>        
    </skill_group>    
  </xsl:template>

</xsl:transform>

排序依据

SELECT * FROM topusers
ORDER BY goldbages DESC
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>

  <!-- Identity Transform -->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>  

  <xsl:template match="stackoverflow">
    <xsl:copy>
      <xsl:apply-templates select="topusers">
        <xsl:sort select="goldbadges" order="descending" data-type="number"/>
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>

</xsl:transform>

案例/时间

SELECT user, membership, link,  
       CASE 
            WHEN membership LIKE '%1 year%' OR membership LIKE '%2 years%'
                 OR membership LIKE '%3 years%'
            THEN 'recent member'
            WHEN membership LIKE '%4 years%' OR membership LIKE '%5 years%'
            THEN 'mid member'
            ELSE 'long member'
       END AS member_type
FROM topusers
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>

  <xsl:template match="/stackoverflow">
    <xsl:copy>      
      <xsl:apply-templates select="topusers"/>
    </xsl:copy>
  </xsl:template>  

  <xsl:template match="topusers">
    <xsl:copy>      
      <xsl:choose>
        <xsl:when test="contains(membership, '1 year') or contains(membership, '2 years')
                        or contains(membership, '3 years')">          
            <xsl:copy-of select="user"/>
            <xsl:copy-of select="membership"/>
            <xsl:copy-of select="link"/>
            <member_type>recent member</member_type>
        </xsl:when>
        <xsl:when test="contains(membership, '4 years') or contains(membership, '5 years')">                      
            <xsl:copy-of select="user"/>
            <xsl:copy-of select="membership"/>
            <xsl:copy-of select="link"/>
            <member_type>mid member</member_type>
        </xsl:when>  
        <xsl:otherwise>                      
            <xsl:copy-of select="user"/>
            <xsl:copy-of select="membership"/>
            <xsl:copy-of select="link"/>
            <member_type>long member</member_type>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:copy>
  </xsl:template>

</xsl:transform>

Java (在 XML 源上运行 XSLT 脚本)

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import javax.xml.transform.*;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.OutputKeys;

import java.io.*;    
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;

import org.w3c.dom.Document;
import org.xml.sax.SAXException;

public class CourseList {
    public static void main(String[] args) throws IOException, URISyntaxException,
                                                  SAXException, ParserConfigurationException,
                                                  TransformerException {

            // Load XML and XSL Document
            String inputXML = "/path/to/Input.xml";
            String xslFile = "/path/to/XSLT/Script.xsl";
            String outputXML = "/path/to/Output.xml";

            Source xslt = new StreamSource(new File(xslFile));            
            DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();            
            DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
            Document doc = docBuilder.parse (new File(inputXML));

            // XSLT Transformation with pretty print
            TransformerFactory prettyPrint = TransformerFactory.newInstance();
            Transformer transformer = prettyPrint.newTransformer(xslt);

            // Dynamic XSLT
            String root = "stackoverflow";
            String TableName = "topusers";
            String Column1 = "user";
            String Column2 = "membership";
            String Column3 = "link";
            String Column4 = "topskill";
            String wherevalue = "java";

            String xslStr = String.join("\n",
                "<xsl:transform xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\">",
                "<xsl:output version=\"1.0\" encoding=\"UTF-8\" indent=\"yes\" />",
                "<xsl:strip-space elements=\"*\"/> ",
                "  <xsl:template match=\"/"+root+"\">",
                "    <xsl:copy>",
                "      <xsl:apply-templates select=\""+TableName+"["+Column4+"='"+wherevalue+"']\"/>",
                "    </xsl:copy>",
                "  </xsl:template>",
                "  <xsl:template match=\""+TableName+"\">",
                "    <xsl:copy>",
                "      <xsl:copy-of select=\""+Column1+"\"/>",
                "      <xsl:copy-of select=\""+Column2+"\"/>",
                "      <xsl:copy-of select=\""+Column3+"\"/>",
                "    </xsl:copy>",
                "  </xsl:template>",
                "</xsl:transform>");

            // Parse XSLT String and Configure Transformer
            Source xslt = new StreamSource(new StringReader(xslStr));
            TransformerFactory prettyPrint = TransformerFactory.newInstance()
            Transformer transformer = prettyPrint.newTransformer(xslt);

            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
            transformer.setOutputProperty(OutputKeys.STANDALONE, "yes");
            transformer.setOutputProperty(OutputKeys.METHOD, "xml");
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");                        

            DOMSource source = new DOMSource(doc);
            StreamResult result = new StreamResult(new File(outputXML));        
            transformer.transform(source, result);
    }
}

关于java - 需要对xml数据执行sql查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40720967/

相关文章:

java - CardLayout语法//.add()

java - gradle没有java方法的签名(hashmap.getOrDefault)适用

c# - SQL 更新查询返回错误

java - 使用scales.xml时如何解决NoSuchMethodError?

c# - 在 ASP.NET (C#) 中创建动态 RSS 提要页面 - 我需要做任何额外的事情吗?

java - 如何使用 CassandraOperations 更新集合

java - 如何保护文件,使其只能由 java 访问?

sql - 预期 slice 但得到界面

php - 使用 switch 元素更新 DB 值

android - 在 cordova 5.2 中传递 config.xml 的问题