【问题标题】:How to use variables in XPath?如何在 XPath 中使用变量?
【发布时间】:2014-01-21 12:41:16
【问题描述】:

我正在使用 Xpath 和 Java。

XML 有大量的OBJECT_TYPES,每个对象类型都有属性和参数。 并且每个属性和参数都有元素。

如何从我的 XML 文件中执行以下操作。 我想知道如何使用 XPATH 字符串表达式选择所有属性元素,具体取决于 OBJECT_TYPE 字符串的名称。对象类型字符串名称取决于用户从列表中选择的名称。

我该怎么做?

应该是这样的:

String expression = "/getObjType()/prop/*"; 

但是getObjectType 是一种方法,所以我不能在字符串表达式中使用它。

XML 看起来像这样:

<type>
  <OBJECT_TYPE>SiteData</OBJECT_TYPE> 
  <prop>
    <DESCRIPTION>Site parameters</DESCRIPTION> 
    <PARENT>NULL</PARENT> 
    <VIRTUAL>0</VIRTUAL> 
    <VISIBLE>1</VISIBLE> 
    <PICTURE>NULL</PICTURE> 
    <HELP>10008</HELP> 
    <MIN_NO>1</MIN_NO> 
    <MAX_NO>1</MAX_NO> 
    <NAME_FORMAT>NULL</NAME_FORMAT> 
  </prop>
  <param>
    <PARAMETER>blabla</PARAMETER> 
    <DATA_TYPE>INTEGER</DATA_TYPE> 
    <DESCRIPTION>blaba</DESCRIPTION> 
    <MIN_NO>1</MIN_NO> 
    <MAX_NO>1</MAX_NO> 
    <ORDER1>1</ORDER1> 
    <NESTED>0</NESTED> 
    <DEFAULT1>NULL</DEFAULT1> 
    <FORMAT>0:16382</FORMAT> 
  </param>
  <OBJECT_TYPE>Data</OBJECT_TYPE> 
  <prop>
    <DESCRIPTION>Site parameters</DESCRIPTION> 
    <PARENT>NULL</PARENT> 
    <VIRTUAL>0</VIRTUAL> 
    <VISIBLE>1</VISIBLE> 
    <PICTURE>NULL</PICTURE> 
    <HELP>10008</HELP> 
    <MIN_NO>1</MIN_NO> 
    <MAX_NO>1</MAX_NO> 
    <NAME_FORMAT>NULL</NAME_FORMAT> 
  </prop>
  <param>
    <PARAMETER>gmgm</PARAMETER> 
    <DATA_TYPE>INTEGER</DATA_TYPE> 
    <DESCRIPTION>babla</DESCRIPTION> 
    <MIN_NO>1</MIN_NO> 
    <MAX_NO>1</MAX_NO> 
    <ORDER1>1</ORDER1> 
    <NESTED>0</NESTED> 
    <DEFAULT1>NULL</DEFAULT1> 
    <FORMAT>0:16382</FORMAT> 
  </param>
</type>

因此,根据 Object_type 的名称,我想获得这些属性,并且我列出了 122 个对象类型,因此我必须使用一个变量来选择用户选择的对象类型。

 public class PropXMLParsing {

    static PropXMLParsing instance = null;

    private List<String> list = new ArrayList<String>();
    ObjType obj = new ObjType();

    public static PropXMLParsing getInstance() {

        if (instance == null) {

            instance = new PropXMLParsing();
            try {
                instance.ParserForObjectTypes();
            } catch (SAXException e) {

                e.printStackTrace();
            } catch (IOException e) {

                e.printStackTrace();
            } catch (ParserConfigurationException e) {

                e.printStackTrace();
            }

        }

        return instance;

    }

    public void ParserForObjectTypes() throws SAXException, IOException,
            ParserConfigurationException {

        try {
            FileInputStream file = new FileInputStream(new File(
                    "xmlFiles/CoreDatamodel.xml"));

            DocumentBuilderFactory builderFactory = DocumentBuilderFactory
                    .newInstance();

            builderFactory.setNamespaceAware(true);
            DocumentBuilder builder = builderFactory.newDocumentBuilder();

            Document xmlDocument = builder.parse(file);

            XPath xp = XPathFactory.newInstance().newXPath();
            final Map<String, Object> vars = new HashMap<String, Object>();
            xp.setXPathVariableResolver(new XPathVariableResolver() {
                public Object resolveVariable(QName name) {
                    return vars.get(name.getLocalPart());
                }
            });

            XPathExpression expr = xp
                    .compile("/type/OBJECT_TYPE[. = $type]/following-sibling::prop[1]");

            vars.put("type", obj.getObjectType());
            NodeList objectProps = (NodeList) expr.evaluate(xmlDocument,
                    XPathConstants.NODESET);
            System.out.println(objectProps);

            for (int i = 0; i < objectProps.getLength(); i++) {

                System.out.println(objectProps.item(i).getFirstChild()
                        .getNodeValue());
                list.add(objectProps.item(i).getFirstChild().getNodeValue());

            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (XPathExpressionException e) {
            e.printStackTrace();
        }
    }

    public String convertListToString() {

        StringBuilder sb = new StringBuilder();
        if (list.size() > 0) {
            sb.append(list.get(0));
            for (int i = 1; i < list.size(); i++) {
                sb.append(list.get(i));
            }
        }
        return sb.toString();
    }

}

第二个解决方案我尝试过既不工作也不在控制台中打印出任何东西。

public void ParserForObjectTypes() throws SAXException, IOException,
            ParserConfigurationException {

        try {
            FileInputStream file = new FileInputStream(new File(
                    "xmlFiles/CoreDatamodel.xml"));

            DocumentBuilderFactory builderFactory = DocumentBuilderFactory
                    .newInstance();

            DocumentBuilder builder = builderFactory.newDocumentBuilder();

            Document xmlDocument = builder.parse(file);

            XPath xPath = XPathFactory.newInstance().newXPath();
            NodeList nodeList = (NodeList) xPath.compile(
                    "//OBJECT_TYPE[text() = '" + obj.getObjectType()
                            + "']/following-sibling::prop[1]/*").evaluate(
                    xmlDocument, XPathConstants.NODESET);

            for (int i = 0; i < nodeList.getLength(); i++) {
                System.out.println(nodeList.item(i).getNodeName() + " = "
                        + nodeList.item(i).getTextContent());
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (XPathExpressionException e) {
            e.printStackTrace();
        }
    }

【问题讨论】:

  • 请添加您尝试查询的 XML 示例。
  • 好的,我已经添加了。我认为不需要。
  • Xpath, Java and variables的可能重复

标签: java xml xpath


【解决方案1】:

如果您想提取属于特定OBJECT_TYPEprop,您可以使用

/type/OBJECT_TYPE[. = 'some type']/following-sibling::prop[1]

在 Java 中,您可以使用字符串连接动态构建此 XPath 表达式,但如果您使用的库可以支持 XPath 变量,则使用该变量会更安全(您不会在问题中说您是哪个库)重新使用)。例如javax.xml.xpath

XPath xp = XPathFactory.newInstance().newXPath();
final Map<String, Object> vars = new HashMap<String, Object>();
xp.setXPathVariableResolver(new XPathVariableResolver() {
  public Object resolveVariable(QName name) {
    return vars.get(name.getLocalPart());
  }
});

XPathExpression expr = xp.compile("/type/OBJECT_TYPE[. = $type]/following-sibling::prop[1]");

vars.put("type", "Data");
Node dataProps = (Node)expr.evaluate(doc, XPathConstants.NODE);

vars.put("type", "SiteData");
Node siteProps = (Node)expr.evaluate(doc, XPathConstants.NODE);

// taking the value from a variable
vars.put("type", obj.getObjectType());
Node objectProps = (Node)expr.evaluate(doc, XPathConstants.NODE);

【讨论】:

  • @Sembrano 您可以在vars.put("type", ...) 电话中提供您喜欢的任何内容。我总是建议你不要通过将用户提供的字符串连接在一起来构建你的 XPath 表达式,它是一个诱人的 XPath 等效于 SQL 注入攻击。
  • @NickHolt 可能“攻击”有点强,但本质上,如果您允许用户提供的输入成为表达式的一部分,那么您甚至无法保证它会 编译 - XPath 的经典示例是无法在一个字符串文字中同时包含单引号和双引号(双引号字符串可以包含单引号,反之亦然,但没有转义机制允许一个字符串包含两种类型)。使用变量解析器,实际的 XPath 表达式是一个编译时常量 - 虽然它可能不匹配,但至少您知道它不会引发异常。
  • @Sembrano 为了使用 XPath,您必须使用可识别名称空间的解析器来解析文档 - 在执行 builderFactory.newDocumentBuilder() 之前调用 builderFactory.setNamespaceAware(true);。除此之外,它应该可以工作,假设您在问题中给出的 XML 是真实结构的真实表示。
  • @Sembrano 您发布的代码不会调用setNamespaceAware(true)
  • @Sembrano 这就是我所说的“假设您在问题中给出的 XML 是真实结构的真实表示” - 如果不是,那么您显然必须调整 XPath 表达式为了匹配,网络上有许多 XPath 教程可以帮助您针对特定情况制定正确的表达式。
【解决方案2】:

此 XPATH 将选择 prop 元素中的所有元素,该元素位于 OBJECT_TYPE 之后,文本为 SiteData

//OBJECT_TYPE[text() = 'SiteData']/following-sibling::prop[1]/*

要更改被选中的OBJECT_TYPE,只需在代码中构造 XPATH:

String xpath = "//OBJECT_TYPE[text() = '" + getObjType() + "']/following-sibling::prop[1]/*"

这会导致如下代码:

XPath xPath =  XPathFactory.newInstance().newXPath();
NodeList nodeList = (NodeList)xPath.compile("//OBJECT_TYPE[text() = '" + getObjType() + "']/following-sibling::prop[1]/*").evaluate(document, XPathConstants.NODESET);

for (int i = 0; i < nodeList.getLength(); i++)
{
  System.out.println(nodeList.item(i).getNodeName() + " = " + nodeList.item(i).getTextContent());
}

给出问题中的 XML 并且当getObjType() 返回SiteData 时打印:

DESCRIPTION = Site parameters
PARENT = NULL
VIRTUAL = 0
VISIBLE = 1
PICTURE = NULL
HELP = 10008
MIN_NO = 1
MAX_NO = 1
NAME_FORMAT = NULL

【讨论】:

  • 不赞成票有什么用 - XPath 有效并解决了问题。
  • 是的,但在我的情况下,除非用户在 gui 中单击它,否则我们不知道名称是 SiteData,因此所有内容都存储在一个变量中,并且我有 getter 和 setter。我做过类似的事情,但还没有测试过:String expression = "/" + obj.getObjectType() + "/prop/*";
  • 那行不通,我回答中示例中的字符串连接将在getObjType() 返回SiteData 或其他有效值之一的地方。如果 XPATH 在用户选择对象类型之前运行,则必须设置默认值,否则 XPATH 可能无效。
  • 我已经尝试过您的解决方案,但控制台中没有写入任何内容。
  • 您如何创建document。我从上面的 XML 中创建了一个String,把它变成了一个byte[] 并调用了Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(xmlBytes))
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-10-05
  • 2012-04-09
  • 2013-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多