Как получить конкретную информацию для файла XML
У меня большой 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&um__1">
<Lemma partOfSpeech="n" writtenForm="تَوَاؤُم"/>
<Sense id="tawaA&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
будь то из <Lemma>
или же <WordForm>
программа принимает значение synset
от <Sense>
того, что writtenForm
(так же <LexicalEntry>
) и ищет все значения id
из <Synset>
которые имеют то же значение, что и synset
от <Sense>
, После этого программа дает нам все отношения этого Synset
т.е. отображает значение relType
и возвращается к <LexicalEntry>
и ищет значение synset
из <Sense>
которые имеют одинаковую ценность targets
затем отображает его writtenForm
,
Я думаю, что это немного сложно, но результат должен быть таким:
اِتِّفاق hyponym تَوَاؤُم, اِنْسِجام
Одним из решений является использование потокового считывателя из-за потребления памяти. но я не знаю, как мне поступить, чтобы получить то, что я хочу. Помоги мне, пожалуйста.
3 ответа
SAX Parser отличается от DOM Parser. Он смотрит только на текущий item
он не может видеть будущие предметы, пока они не станут текущими item
, Это один из многих, которые вы можете использовать, когда XML-файл очень большой. Вместо этого есть много там. Назвать несколько:
SAX
PARSERDOM
PARSERJDOM
PARSERDOM4J
PARSERSTAX
PARSER
Вы можете найти для них учебники здесь.
На мой взгляд, после изучения сразу перейти к использованию DOM4J
или же JDOM
для коммерческого продукта.
Логика SAX
Парсер в том что у тебя есть 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();
}
}
}
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. Части могут быть любой структурной частью документа: текст, элементы, атрибуты, даже такие вещи, как комментарии.
- Выражение Java XPath может пытаться сопоставить ровно одну часть или несколько частей или даже объединить все совпадающие части в виде строки.
- В выражении XPath имя само по себе представляет элемент. Например,
WordForm
в XPath значит любой<WordForm …>
элемент в документе XML. - Имя, начинающееся с
@
представляет атрибут. Например,@writtenForm
относится к любомуwrittenForm=…
атрибут в документе XML. - Косая черта указывает на родителя и потомка в документе XML.
LexicalEntry/Lemma
означает любой<Lemma>
элемент, который является прямым потомком<LexicalEntry>
элемент.Synset/@id
означаетid=…
атрибут любого<Synset>
элемент. - Так же, как путь, начинающийся с
/
указывает абсолютный (относительный к корню) путь в Unix, XPath, начинающийся с косой черты, обозначает выражение относительно корня XML-документа. - Две косые черты означают потомка, который может быть прямым ребенком, внуком, правнуком и т. Д. Таким образом,
//LexicalEntry
означает любой LexicalEntry в документе;/LexicalEntry
соответствует только элементу LexicalEntry, который является корневым элементом. - Квадратные скобки указывают на квалификаторы соответствия.
Synset[@baseConcept='3']
соответствует любому<Synset>
элемент с атрибутом baseConcept, значением которого является строка "3". - XPath может ссылаться на переменные, которые определены внешне, используя Unix-shell-like
$
замены, как$word
, То, как эти переменные передаются в выражение XPath, зависит от движка. Java использует метод setXPathVariableResolver. Имена переменных находятся в совершенно отдельном пространстве имен от имен узлов, поэтому не имеет значения, если имя переменной совпадает с именем элемента или именем атрибута в документе XML.
Итак, выражения XPath в коде означают:
//LexicalEntry[WordForm/@writtenForm=$word or Lemma/@writtenForm=$word]/Sense/@synset
Подходим любой <LexicalEntry>
элемент в любом месте документа XML, который имеет
- дочерний элемент WordForm с атрибутом writForm, значение которого равно
word
переменная - дочерний элемент леммы с атрибутом wroteForm, значение которого равно
word
переменная
и для каждого такого <LexicalEntry>
элемент, вернуть значение synset
атрибут любого <Sense>
элемент, который является прямым потомком <LexicalEntry>
элемент.
word
переменная определяется внешне, xpath.setXPathVariableResolver
, прямо перед вычислением выражения XPath.
//Synset[@id=$id]/SynsetRelations/SynsetRelation
Подходим любой <Synset>
элемент в любом месте документа XML, чьи id
атрибут равен id
переменная. Для каждого такого <Synset>
элемент, найдите любой прямой дочерний элемент SynsetRelations и верните каждый его прямой дочерний элемент SynsetRelation.
id
переменная определяется внешне, xpath.setXPathVariableResolver
, прямо перед вычислением выражения XPath.
//LexicalEntry[Sense/@synset=$synset]/WordForm/@writtenForm
Подходим любой <LexicalEntry>
элемент в любом месте документа XML, который имеет <Sense>
дочерний элемент, который имеет synset
атрибут, значение которого совпадает с synset
переменная. Для каждого соответствующего элемента найдите любой <WordForm>
дочерний элемент и вернуть этот элемент writtenForm
приписывать.
synset
переменная определяется внешне, xpath.setXPathVariableResolver
, прямо перед вычислением выражения XPath.
Логично, что вышеупомянутое должно составить:
- Найдите значение synset для запрошенного слова.
- Используйте значение synset, чтобы найти элементы SynsetRelation.
- Найдите значения записанной формы, соответствующие целевому значению каждого соответствующего SynsetRelation.
Если этот XML-файл слишком велик для представления в памяти, используйте SAX.
Вы захотите написать свой синтаксический анализатор SAX, чтобы сохранить местоположение. Для этого я обычно использую StringBuffer, но стек строк будет работать так же хорошо. Эта часть будет важна, потому что она позволит вам отслеживать путь до корня документа, что позволит вам понять, где в документе вы находитесь в данный момент времени (полезно при попытке извлечь только мало информации).
Основной логический поток выглядит так:
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, которые вас не интересуют.