Java: как разбить XML-поток на небольшие XML-документы с родительским узлом. VTD-XML

Мне нужно прочитать большой xml с VTD XML и XPath и разделить результаты на несколько узлов. Я нашел несколько решений здесь, но он разделяет узлы, но без информации о родителях.

Что, почему я ищу:

Строка XPath: /CATALOG/MAIN/CD На основе XPath документ должен быть разделен

1) Исходный документ:

<CATALOG> <MAIN id="1"> <CD> <TITLE>Empire Burlesque</TITLE> <ARTIST>Bob Dylan</ARTIST> </CD> <CD> <TITLE>Empire Dummy</TITLE> <ARTIST>John Doe</ARTIST> </CD> <USEFUL>Useful node</USEFUL> </MAIN> <MAIN id="2"> <CD> <TITLE>Still got the blues</TITLE> <ARTIST>Gary More</ARTIST> </CD> </MAIN> <IGNORED>Ignored node</IGNORED> </CATALOG>

2) Результаты: Документ 1:

<CATALOG> <MAIN id="1"> <CD> <TITLE>Empire Burlesque</TITLE> <ARTIST>Bob Dylan</ARTIST> </CD> <USEFUL>Useful node</USEFUL> </MAIN> </CATALOG>

Документ 2:

<CATALOG> <MAIN id="1"> <CD> <TITLE>Empire Dummy</TITLE> <ARTIST>John Doe</ARTIST> </CD> <USEFUL>Useful node</USEFUL> </MAIN> </CATALOG>

Документ 3:

<CATALOG> <MAIN id="2"> <CD> <TITLE>Still got the blues</TITLE> <ARTIST>Gary More</ARTIST> </CD> </MAIN> </CATALOG>

Спасибо за ваше время и предложения.

С наилучшими пожеланиями!


person Socolov Vladislav    schedule 12.12.2014    source источник
comment
взгляните на это (stackoverflow.com/ вопросы/2056910/)   -  person Bhargav Modi    schedule 12.12.2014
comment
Вы имеете в виду добавление корневого элемента впереди? Вы должны быть в состоянии сделать это программно .. можете ли вы?   -  person vtd-xml-author    schedule 13.12.2014
comment
Да, я имею в виду добавление корневого элемента, но без родственного узла. Мне нужны полезные ссылки, как это сделать с vtd-xml   -  person Socolov Vladislav    schedule 13.12.2014
comment
Я могу придумать другой фрагмент кода, который делает то, что вы описали, но тогда вы можете вернуться с более сложными вариантами использования... :(   -  person vtd-xml-author    schedule 15.12.2014
comment
Я не уверен, что существует более сложный вариант использования для разделения. Если честно, я не уверен, что это возможно сделать с помощью парсера на основе SAX. Я реализовал это с помощью стандартных манипуляций с DOM и рекурсии Node, но это ужасно медленно :( .   -  person Socolov Vladislav    schedule 15.12.2014


Ответы (3)


Ниже приведен код, который делает то, что вы описали в vtd-xml. дайте мне знать, если есть какие-либо вопросы.

import com.ximpleware.*;
import java.io.FileOutputStream;

public class splitTest {

    public static void  main(String[] a) throws VTDException,java.io.IOException{
        VTDGen vg = new VTDGen();
        if (vg.parseFile("C:\\Users\\Jimmy Zhang\\workspace\\ximple-dev\\DOMTest\\test111.xml", false)){
            VTDNav vn = vg.getNav();
            AutoPilot ap = new AutoPilot(vn);
            ap.selectXPath("/CATALOG/MAIN");
            byte[] header = "<CATALOG>".getBytes();
            byte[] tail = "</CATALOG>".getBytes();
            int i = -1,j=0;
            while((i=ap.evalXPath())!=-1){
                long l = vn.getElementFragment();
                FileOutputStream fops = new FileOutputStream("c:\\xml\\output"+j+".xml");
                fops.write(header);
                fops.write(vn.getXML().getBytes(), (int)l, ((int)(l>>32)));
                fops.write(tail);
                fops.close();
                j++;
            }

        }
    }
}
person vtd-xml-author    schedule 13.12.2014
comment
Спасибо за ваш подход, но мне нужна более сложная логика разделения, и я не уверен, что SAX или VTD-XML могут ее выполнить. Ваш подход потерпит неудачу, если я добавлю еще один узел CD друг к другу. Я отредактировал свой вопрос с полным примером. - person Socolov Vladislav; 14.12.2014
comment
Я думаю, что логика разделения является наиболее важной вещью здесь, vtd-xml - это только инструмент, и приложение, которое вы создаете, в первую очередь управляется логикой разделения здесь, а не самими инструментами .... это может оказаться значительным кусок кода... - person vtd-xml-author; 15.12.2014
comment
@vtd-xml-author, каким должен быть подход, если мое разделение должно основываться на размере или количестве узлов в файле. Пример: 1-й разделенный файл должен содержать 500 узлов, поэтому второй. - person Jay; 07.07.2016

Вот мой подход...

Используйте некоторые библиотеки синтаксического анализа xml, например javax.xml.parsers.DocumentBuilderFactory. Создайте DOM для входного xml-файла... Для каждого встречающегося узла создайте новый выходной файл Document.xml, например, Document1.xml (добавьте дочерний узел в родительский.

Вы можете сослаться на http://www.programcreek.com/java-api-examples/index.php?api=javax.xml.parsers.DocumentBuilderFactory для примера кода Java для анализа XML с использованием пакета java.xml.parsers.* (ищите пример loadQuestions)

person Balaji Katika    schedule 12.12.2014
comment
Я уже реализовал стандартную программу чтения XPath и DocumentBuilderFactory, но это слишком медленно. Мне нужен более быстрый подход, основанный на Sax и VTD-XML. - person Socolov Vladislav; 12.12.2014

Я решил свою проблему. Вот мой подход, основанный на стандартном разборе SAX.

1) Создал пользовательский SaxHandler:

` открытый класс CustomSAXHandler расширяет DefaultHandler {

private Stack<XmlNodeInfo> nodeStack = new Stack<XmlNodeInfo>();
private List<String> xPaths;
private XmlNodeInfo rootNode;
private final NamespaceContext namespaceContext;
private List<XmlNodeInfo> resultNodes;

public CustomSAXHandler(String xpath, XmlNodeInfo rootNode, NamespaceContext namespaceContext) {
    this.rootNode = rootNode;
    this.namespaceContext = namespaceContext;
    resultNodes = new ArrayList<XmlNodeInfo>();
    xPaths = splitXpaths(xpath);
}

@Override
public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
    String element = "<" + qName + getAttributes(atts) + ">";
    if (!nodeStack.empty()) {
        rootNode = nodeStack.peek();
    }

    if (matchDefinedXpath(qName)) {
        XmlNodeInfo newNode = new XmlNodeInfo(qName);
        rootNode.addChild(newNode);
        nodeStack.push(newNode);

        newNode.getHeader().append(element);
    } else {
        if (!nodeStack.empty()) {
            nodeStack.peek().getBody().append(element);
        }
    }
}

@Override
public void characters(char[] ch, int start, int length) throws SAXException {
    XmlNodeInfo currentNode = nodeStack.empty() ? null : nodeStack.peek();
    if (currentNode != null) {
        currentNode.getBody().append(new String(ch, start, length));
    }
}

@Override
public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
    String finalElement = xPaths.get(xPaths.size() - 1);
    String element = "</" + qName + ">";
    XmlNodeInfo currentNode = nodeStack.empty() ? null : nodeStack.peek();
    if (currentNode != null) {
        if (qName.equals(finalElement) && nodeStack.size() == xPaths.size()) {
            currentNode.getFooter().append(element);
            resultNodes.add(currentNode);
            nodeStack.pop();
        } else {
            if (currentNode.getName().equals(qName)) {
                currentNode.getFooter().append(element);
                nodeStack.pop();
            } else {
                currentNode.getBody().append(element);
            }
        }
    }
}

public List<String> getResults() {
    List<String> results = new ArrayList<String>();

    for (XmlNodeInfo node : resultNodes) {
        buildDocument(node, null, results);
    }

    return results;
}

private void buildDocument(XmlNodeInfo node, String childContent, List<String> results) {
    String body = node.getBody().toString();
    if (childContent != null) {
        body = body + childContent;
    }
    if (node.getParent() != null && !node.getParent().getName().equals(XmlNodeInfo.ROOT_NODE_NAME)) {
        String xmlContent = String.valueOf(node.getHeader()) + body + node.getFooter();
        buildDocument(node.getParent(), xmlContent, results);
    } else if (node.getParent() != null && node.getParent().getName().equals(XmlNodeInfo.ROOT_NODE_NAME)) {
        String finalContent = String.valueOf(node.getHeader()) + body + node.getFooter();
        results.add(finalContent);
    }
}

private String getAttributes(Attributes atts) {
    StringBuilder builder = new StringBuilder();
    for (int i = 0; i < atts.getLength(); i++) {
        String qName = atts.getQName(i);
        String value = atts.getValue(qName);
        builder.append(" ").append(qName).append("=").append("\"").append(value).append("\"");
    }
    return builder.toString();
}

private boolean matchDefinedXpath(String nodeName) {
    String[] splitWords = nodeName.split(":");
    if (splitWords.length == 2) {
        String namespacePrefix = splitWords[0];
        String namespaceURI = namespaceContext.getNamespaceURI(namespacePrefix);
        Iterator prefixes = namespaceContext.getPrefixes(namespaceURI);
        while (prefixes.hasNext()) {
            String prefix = (String) prefixes.next();
            String elementName = prefix + ":" + splitWords[1];
            if (xPaths.contains(elementName)) {
                return true;
            }
        }
    } else {
        return xPaths.contains(nodeName);
    }
    return false;
}

private List<String> splitXpaths(String xPath) {
    if (StringUtils.isNotBlank(xPath)) {
        String[] splitWords = xPath.split("/");
        if (splitWords.length > 0) {
            List<String> results = new ArrayList<String>();
            for (String splitWord : splitWords) {
                if(StringUtils.isNotBlank(splitWord)){
                    results.add(splitWord);
                }
            }
            return results;
        }
    }
    return null;
}

}

`

2) Создайте bean-компонент для хранения данных узла:

`

открытый класс XmlNodeInfo {

public static final String ROOT_NODE_NAME = "ROOT";

private String name;
private StringBuilder header;
private StringBuilder body;
private StringBuilder footer;
private List<XmlNodeInfo> children;
private XmlNodeInfo parent;

public XmlNodeInfo(String name) {
    this.name = name;
    header = new StringBuilder();
    body = new StringBuilder();
    footer = new StringBuilder();
    children = new ArrayList<XmlNodeInfo>();
}

public StringBuilder getHeader() {
    return header;
}

public StringBuilder getBody() {
    return body;
}

public StringBuilder getFooter() {
    return footer;
}

public List<XmlNodeInfo> getChildren() {
    return children;
}

public void addChild(XmlNodeInfo xmlNodeInfo) {
    children.add(xmlNodeInfo);
    xmlNodeInfo.setParent(this);
}

public String getName() {
    return name;
}

public XmlNodeInfo getParent() {
    return parent;
}

public void setParent(XmlNodeInfo parent) {
    this.parent = parent;
}

}

`

3) Запустить программу:

`общедоступный класс MainApp {

public static void main(String[] args) throws Exception {
    SAXParserFactory factory = SAXParserFactory.newInstance();
    SAXParser saxParser = factory.newSAXParser();

    NamespaceContext namespaceContext = new XmlNamespaceResolver();
    String xPath = "/CATALOG/MAIN/CD";

    InputStream in = MainApp.class.getClassLoader().getResourceAsStream("test.xml");
    XmlNodeInfo rootNode = new XmlNodeInfo(XmlNodeInfo.ROOT_NODE_NAME);
    CustomSAXHandler customSAXHandler = new CustomSAXHandler(xPath, rootNode, namespaceContext);
    saxParser.parse(in, customSAXHandler);
    List<String> results = customSAXHandler.getResults(); // result strings
}

}

`

Возможно, это не лучшее решение, но оно решает мои проблемы. Спасибо всем за ваше время и предложения.

person Socolov Vladislav    schedule 15.12.2014
comment
Просто любопытно, он сохраняет «полезный» узел? - person vtd-xml-author; 17.12.2014
comment
Конечно. Он сохраняет все родительские узлы, кроме узлов, представленных в XPath. - person Socolov Vladislav; 17.12.2014