【问题标题】:How to get a specifc information for an XML file如何获取 XML 文件的特定信息
【发布时间】:2016-12-22 14:51:44
【问题描述】:

我有一个大的XML 文件,下面是其中的摘录:

...
<LexicalEntry id="Ait~ifAq_1">
  <Lemma partOfSpeech="n" writtenForm="اِتِّفاق"/>
  <Sense id="Ait~ifAq_1_tawaAfuq_n1AR" synset="tawaAfuq_n1AR"/>
  <WordForm formType="root" writtenForm="وفق"/>
</LexicalEntry>
<LexicalEntry id="tawaA&amp;um__1">
  <Lemma partOfSpeech="n" writtenForm="تَوَاؤُم"/>
  <Sense id="tawaA&amp;um__1_AinosijaAm_n1AR" synset="AinosijaAm_n1AR"/>
  <WordForm formType="root" writtenForm="وأم"/>
</LexicalEntry>    
<LexicalEntry id="tanaAgum_2">
  <Lemma partOfSpeech="n" writtenForm="تناغُم"/>
  <Sense id="tanaAgum_2_AinosijaAm_n1AR" synset="AinosijaAm_n1AR"/>
  <WordForm formType="root" writtenForm="نغم"/>
</LexicalEntry>


<Synset baseConcept="3" id="tawaAfuq_n1AR">
  <SynsetRelations>
    <SynsetRelation relType="hyponym" targets="AinosijaAm_n1AR"/>
    <SynsetRelation relType="hyponym" targets="AinosijaAm_n1AR"/>
    <SynsetRelation relType="hypernym" targets="ext_noun_NP_420"/>
  </SynsetRelations>
  <MonolingualExternalRefs>
    <MonolingualExternalRef externalReference="13971065-n" externalSystem="PWN30"/>
  </MonolingualExternalRefs>
</Synset>
...

我想从中提取特定信息。对于给定的writtenForm,无论是来自&lt;Lemma&gt; 还是&lt;WordForm&gt;,程序都会从​​&lt;Sense&gt;writtenForm(相同的&lt;LexicalEntry&gt;)中获取synset 的值,并搜索所有id 的值&lt;Synset&gt; 与来自&lt;Sense&gt;synset 具有相同的值。之后,程序给我们那个Synset的所有关系,即它显示relType的值并返回&lt;LexicalEntry&gt;并寻找&lt;Sense&gt;的值synset与@的值相同987654339@ 然后显示其writtenForm

我觉得有点复杂,但结果应该是这样的:

اِتِّفاق hyponym تَوَاؤُم, اِنْسِجام

其中一个解决方案是使用 Stream 阅读器,因为它会消耗内存。但我不知道我应该如何继续得到我想要的。请帮帮我。

【问题讨论】:

  • 对不起,但实际上我没有尝试任何东西,因为我不知道该使用什么。我确实提到我可以使用 Stream 阅读器,但是如何使用??
  • 先阅读基础教程
  • 您想为此使用现成的库还是要编写自己的库?
  • 如果可能,为什么不呢?如果有其他解决方案,我会很高兴听到它。

标签: java parsing xlm


【解决方案1】:

SAX 解析器与 DOM 解析器不同。它只查看当前的 item,在它们变为当前的 item 之前无法查看未来的项目。当 XML 文件非常大时,它是您可以使用的众多工具之一。取而代之的是很多。仅举几例:

  • SAX解析器
  • DOM解析器
  • JDOM解析器
  • DOM4J解析器
  • STAX解析器

您可以找到所有这些教程here

在我看来,学习后直接使用DOM4JJDOM 进行商业产品。

SAX Parser 的逻辑是你有一个 MyHandler 类,它扩展了 DefaultHandler@Overrides 它的一些方法:

XML 文件:

<?xml version="1.0"?>
<class>
   <student rollno="393">
      <firstname>dinkar</firstname>
      <lastname>kad</lastname>
      <nickname>dinkar</nickname>
      <marks>85</marks>
   </student>
   <student rollno="493">
      <firstname>Vaneet</firstname>
      <lastname>Gupta</lastname>
      <nickname>vinni</nickname>
      <marks>95</marks>
   </student>
   <student rollno="593">
      <firstname>jasvir</firstname>
      <lastname>singn</lastname>
      <nickname>jazz</nickname>
      <marks>90</marks>
   </student>
</class>

处理程序类:

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class UserHandler extends DefaultHandler {

   boolean bFirstName = false;
   boolean bLastName = false;
   boolean bNickName = false;
   boolean bMarks = false;

   @Override
   public void startElement(String uri, 
   String localName, String qName, Attributes attributes)
      throws SAXException {
      if (qName.equalsIgnoreCase("student")) {
         String rollNo = attributes.getValue("rollno");
         System.out.println("Roll No : " + rollNo);
      } else if (qName.equalsIgnoreCase("firstname")) {
         bFirstName = true;
      } else if (qName.equalsIgnoreCase("lastname")) {
         bLastName = true;
      } else if (qName.equalsIgnoreCase("nickname")) {
         bNickName = true;
      }
      else if (qName.equalsIgnoreCase("marks")) {
         bMarks = true;
      }
   }

   @Override
   public void endElement(String uri, 
   String localName, String qName) throws SAXException {
      if (qName.equalsIgnoreCase("student")) {
         System.out.println("End Element :" + qName);
      }
   }

   @Override
   public void characters(char ch[], 
      int start, int length) throws SAXException {
      if (bFirstName) {
         System.out.println("First Name: " 
            + new String(ch, start, length));
         bFirstName = false;
      } else if (bLastName) {
         System.out.println("Last Name: " 
            + new String(ch, start, length));
         bLastName = false;
      } else if (bNickName) {
         System.out.println("Nick Name: " 
            + new String(ch, start, length));
         bNickName = false;
      } else if (bMarks) {
         System.out.println("Marks: " 
            + new String(ch, start, length));
         bMarks = false;
      }
   }
}

类:

import java.io.File;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class SAXParserDemo {
   public static void main(String[] args){

      try { 
         File inputFile = new File("input.txt");
         SAXParserFactory factory = SAXParserFactory.newInstance();
         SAXParser saxParser = factory.newSAXParser();
         UserHandler userhandler = new UserHandler();
         saxParser.parse(inputFile, userhandler);     
      } catch (Exception e) {
         e.printStackTrace();
      }
   }   
}

【讨论】:

  • 谢谢。我认为现在很清楚。因此,如果我想从文件中提取信息,我应该根据需要操作 startElement 方法吗?
  • @bttX 您必须操作 Handler 中的所有方法。每一个都是为了每个自己的目的。您可以检测到从 @propertiescomments 到每一个 Element 的所有内容。我建议你先看教程(你需要它们)。
  • 谢谢。你有什么教程建议我开始吗?
【解决方案2】:

XPath 正是为此而设计的。 Java 在javax.xml.xpath 包中提供了对它的支持。

做你想做的,代码看起来像这样:

List<String> findRelations(String word,
                           Path xmlFile)
throws XPathException {

    String xmlLocation = xmlFile.toUri().toASCIIString();

    XPath xpath = XPathFactory.newInstance().newXPath();

    xpath.setXPathVariableResolver(
        name -> (name.getLocalPart().equals("word") ? word : null));
    String id = xpath.evaluate(
        "//LexicalEntry[WordForm/@writtenForm=$word or Lemma/@writtenForm=$word]/Sense/@synset",
        new InputSource(xmlLocation));

    xpath.setXPathVariableResolver(
        name -> (name.getLocalPart().equals("id") ? id : null));
    NodeList matches = (NodeList) xpath.evaluate(
        "//Synset[@id=$id]/SynsetRelations/SynsetRelation",
        new InputSource(xmlLocation),
        XPathConstants.NODESET);

    List<String> relations = new ArrayList<>();

    int matchCount = matches.getLength();
    for (int i = 0; i < matchCount; i++) {
        Element match = (Element) matches.item(i);

        String relType = match.getAttribute("relType");
        String synset = match.getAttribute("targets");

        xpath.setXPathVariableResolver(
            name -> (name.getLocalPart().equals("synset") ? synset : null));
        NodeList formNodes = (NodeList) xpath.evaluate(
            "//LexicalEntry[Sense/@synset=$synset]/WordForm/@writtenForm",
            new InputSource(xmlLocation),
            XPathConstants.NODESET);

        int formCount = formNodes.getLength();
        StringJoiner forms = new StringJoiner(",");
        for (int j = 0; j < formCount; j++) {
            forms.add(
                formNodes.item(j).getNodeValue());
        }

        relations.add(
            String.format("%s %s %s", word, relType, forms));
    }

    return relations;
}

一些基本的 XPath 信息:

  • XPath 使用单个类似文件路径的字符串来匹配 XML 文档的各个部分。这些部分可以是文档的任何结构部分:文本、元素、属性,甚至是 cmets 之类的东西。
  • Java XPath 表达式可以尝试精确匹配一个或多个部分,甚至可以将所有匹配的部分连接为字符串。
  • 在 XPath 表达式中,名称本身代表一个元素。例如,XPath 中的 WordForm 表示 XML 文档中的任何 &lt;WordForm …&gt; 元素。
  • @ 开头的名称代表一个属性。例如,@writtenForm 指的是 XML 文档中的任何 writtenForm=… 属性。
  • 斜线表示 XML 文档中的父项和子项。 LexicalEntry/Lemma 表示任何&lt;Lemma&gt; 元素,它是&lt;LexicalEntry&gt; 元素的直接子元素。 Synset/@id 表示任何&lt;Synset&gt; 元素的id=… 属性。
  • 正如以/ 开头的路径在 Unix 中表示绝对(根相对)路径一样,以斜杠开头的 XPath 表示相对于 XML 文档根的表达式。
  • 两个斜线表示一个后代,可以是直子、孙子、曾孙等。因此,//LexicalEntry 表示文档中的任何 LexicalEntry; /LexicalEntry 只匹配作为根元素的 LexicalEntry 元素。
  • 方括号表示匹配限定符。 Synset[@baseConcept='3'] 匹配任何带有 baseConcept 属性的 &lt;Synset&gt; 元素,该属性的值为字符串“3”。
  • XPath 可以引用外部定义的变量,使用类似 Unix-shell 的 $ 替换,如 $word。这些变量如何传递给 XPath 表达式取决于引擎。 Java 使用setXPathVariableResolver 方法。变量名称与节点名称位于完全不同的命名空间中,因此如果变量名称与 XML 文档中的元素名称或属性名称相同,则无关紧要。

所以,代码中的 XPath 表达式的意思是:

//LexicalEntry[WordForm/@writtenForm=$word or Lemma/@writtenForm=$word]/Sense/@synset

匹配 XML 文档中任意位置的任何 &lt;LexicalEntry&gt; 元素

  • 具有writtenForm 属性的WordForm 子项,其值等于word 变量
  • 具有writtenForm 属性的引理子项,其值等于word 变量

对于每个这样的&lt;LexicalEntry&gt; 元素,返回任何&lt;Sense&gt; 元素的synset 属性的值,该元素是&lt;LexicalEntry&gt; 元素的直接子元素。

word 变量由xpath.setXPathVariableResolver 在外部定义,就在 XPath 表达式计算之前。

//Synset[@id=$id]/SynsetRelations/SynsetRelation

匹配 XML 文档中任何id 属性等于id 变量的任何&lt;Synset&gt; 元素。对于每个这样的 &lt;Synset&gt; 元素,查找任何直接 SynsetRelations 子元素,并返回其每个直接 SynsetRelation 子元素。

id 变量由xpath.setXPathVariableResolver 在外部定义,就在 XPath 表达式计算之前。

//LexicalEntry[Sense/@synset=$synset]/WordForm/@writtenForm

匹配 XML 文档中任意位置的任何 &lt;LexicalEntry&gt; 元素,该元素具有 &lt;Sense&gt; 子元素,该子元素具有 synset 属性,其值与 synset 变量相同。对于每个匹配的元素,找到任何 &lt;WordForm&gt; 子元素并返回该元素的 writtenForm 属性。

synset 变量在 XPath 表达式求值之前由 xpath.setXPathVariableResolver 外部定义。


从逻辑上讲,以上内容应该是:

  • 找到所请求单词的同义词集值。
  • 使用 synset 值定位 SynsetRelation 元素。
  • 找到与每个匹配的 SynsetRelation 的目标值相对应的writtenForm 值。

【讨论】:

  • 谢谢,但是这段代码让我得到了重复的结果[اِتِّفاق hyponym سجم,نغم,نسق,وأم,وأم, اِتِّفاق hyponym سجم,نغم,نسق,وأم,وأم, اِتِّفاق hyponym سجم,نغم,نسق,وأم,وأم, اِتِّفاق hyponym سجم,نغم,نسق,وأم,وأم, اِتِّفاق hypernym ]我不熟悉XPATH所以你能在代码中添加一些cmets
  • @bttX 更新了答案并解释了 XPath 表达式。
  • 现在说得通了。谢谢你。所以,从现在开始,如果我想定位某个属性的任何特定值,我应该使用XPath?
  • 除非您的 XML 文档非常简单,否则 XPath 通常是定位元素或属性的最简单方法,是的。
  • Define simple :) 那是什么意思?
【解决方案3】:

如果此 XML 文件太大而无法在内存中表示,请使用 SAX。

您需要编写 SAX 解析器来维护位置。为此,我通常使用 StringBuffer,但 Stack of Strings 也可以很好地工作。这部分很重要,因为它将允许您跟踪返回文档根目录的路径,这将允许您了解在给定时间点您在文档中的位置(在尝试仅提取信息很少)。

主要逻辑流程如下:

 1. When entering a node, add the node's name to the stack.
 2. When exiting a node, pop the node's name (top element) off the stack.
 3. To know your location, read your current branch of the XML from the bottom of the stack to the top of the stack.
 4. When entering a region you care about, clear the buffer you will capture the characters into
 5. When exiting a region you care about, flush the buffer into the data structure you will return back as your output.

这样您可以有效地跳过您不关心的 XML 树的所有分支。

【讨论】:

  • 一开始,我创建了我的parser 和我的handle,当我开始覆盖startElement 方法时,我不知道该放什么以及从哪里开始!
  • docs.oracle.com/javase/tutorial/jaxp/sax/parsing.html 是一个很好的起点。如果你想使用这些工具包,你就无法绕过学习它们,在你学习了工具包之后,你会意识到使用 SAX 你必须自己做很多事情(这是我上面的 cmets 将为你提供最多的地方实用程序)。
  • 对不起,我不明白你的 cmets。我应该将它们与SAX 一起使用还是另一种方法。我在这里很困惑
  • 一旦您对 SAX 有所了解,那么我的说明将为您提供有关如何使用 SAX 解决问题的更高层次的指导。基本上我说的是如何用砖块建造拱门,但在尝试第一个拱门之前,您首先需要知道如何将砖块粘在一起。 :)
  • 我阅读了您给我的文档以及其他一些文档,我发现我必须将我需要的内容放入 startElement 方法中,对吗?我还需要按照您的指示这样做吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-28
  • 1970-01-01
  • 2021-12-05
  • 1970-01-01
  • 2022-01-28
  • 2014-12-16
相关资源
最近更新 更多