Как извлечь дочерний элемент из XML в строку в Java?

Если у меня есть документ XML, как

<root>   
   <element1>
        <child attr1="blah">
           <child2>blahblah</child2>
        <child>   
   </element1> 
</root>

Я хочу получить строку XML с первым дочерним элементом. Моя выходная строка будет

<element1>
    <child attr1="blah">
       <child2>blahblah</child2>
    <child>
</element1>

Есть много подходов, хотелось бы увидеть некоторые идеи. Я пытался использовать API Java XML для этого, но не ясно, есть ли хороший способ сделать это.

Спасибо

8 ответов

Решение

Вы правы, со стандартным XML API, нет хорошего пути - вот один пример (может быть, с ошибками; он работает, но я написал это давно).

import javax.xml.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import org.w3c.dom.*;
import java.io.*;

public class Proc
{
    public static void main(String[] args) throws Exception
    {
        //Parse the input document
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.parse(new File("in.xml"));

        //Set up the transformer to write the output string
        TransformerFactory tFactory = TransformerFactory.newInstance();
        Transformer transformer = tFactory.newTransformer();
        transformer.setOutputProperty("indent", "yes");
        StringWriter sw = new StringWriter();
        StreamResult result = new StreamResult(sw);

        //Find the first child node - this could be done with xpath as well
        NodeList nl = doc.getDocumentElement().getChildNodes();
        DOMSource source = null;
        for(int x = 0;x < nl.getLength();x++)
        {
            Node e = nl.item(x);
            if(e instanceof Element)
            {
                source = new DOMSource(e);
                break;
            }
        }

        //Do the transformation and output
        transformer.transform(source, result);
        System.out.println(sw.toString());
    }
}

Казалось бы, вы можете получить первый дочерний элемент, просто используя doc.getDocumentElement(). GetFirstChild(), но проблема в том, что если между корневым элементом и дочерним элементом есть пробелы, это создаст узел Text в дерево, и вы получите этот узел вместо фактического элемента узла. Выход из этой программы:

D:\home\tmp\xml>java Proc
<?xml version="1.0" encoding="UTF-8"?>
<element1>
        <child attr1="blah">
           <child2>blahblah</child2>
       </child>
   </element1>

Я думаю, что вы можете подавить строку версии XML, если вам это не нужно, но я не уверен в этом. Возможно, я бы попытался использовать стороннюю библиотеку XML, если это вообще возможно.

Так как это топовый ответ Google и для тех из вас, кто просто хочет базовое:

    public static String serializeXml(Element element) throws Exception
{
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    StreamResult result = new StreamResult(buffer);

    DOMSource source = new DOMSource(element);
    TransformerFactory.newInstance().newTransformer().transform(source, result);

    return new String(buffer.toByteArray());
}

Я использую это для отладки, которая, скорее всего, то, что вам нужно для этого

Я бы порекомендовал JDOM. Это библиотека Java XML, которая делает работу с XML намного проще, чем стандартный подход W3C.

public String getXML(String xmlContent, String tagName){

    String startTag = "<"+ tagName + ">";
    String endTag = "</"+ tagName + ">";
    int startposition = xmlContent.indexOf(startTag);
    int endposition = xmlContent.indexOf(endTag, startposition);
    if (startposition == -1){
        return "ddd";
    }
    startposition += startTag.length();
    if(endposition == -1){ 
        return "eee";
    }
    return xmlContent.substring(startposition, endposition);
}

Передайте свой xml как строку этому методу, а в вашем случае передайте 'element' в качестве параметра tagname.

XMLBeans - это простой в использовании (как только вы его освоите) инструмент для работы с XML без необходимости разбираться с неприятностями при разборе.

Для этого требуется, чтобы у вас была схема для файла XML, но он также предоставляет инструмент для генерации схемы из файла XML exisint (в зависимости от ваших потребностей сгенерированный файл, вероятно, подойдет).

Вы можете использовать следующую функцию, чтобы извлечь блок xml как строку, передав правильное выражение xpath,

    private static String nodeToString(Node node) throws TransformerException
{
    StringWriter buf = new StringWriter();
    Transformer xform = TransformerFactory.newInstance().newTransformer();
    xform.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
    xform.transform(new DOMSource(node), new StreamResult(buf));
    return(buf.toString());
}

    public static void main(String[] args) throws Exception
{
        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
        Document doc = dBuilder.parse(inputFile);

        XPath xPath = XPathFactory.newInstance().newXPath();
        Node result = (Node)xPath.evaluate("A/B/C", doc, XPathConstants.NODE); //"A/B[id = '1']" //"//*[@type='t1']"

        System.out.println(nodeToString(result));

}

Поскольку вопрос на самом деле касается первого появления строки внутри другой строки, я бы использовал методы класса String вместо анализаторов XML:

public static String getElementAsString(String xml, String tagName){
    int beginIndex = xml.indexOf("<" + tagName);
    int endIndex = xml.indexOf("</" + tagName, beginIndex) + tagName.length() + 3;
    return xml.substring(beginIndex, endIndex);
}

Если ваша xml поддерживает схему, вы можете использовать xmlbeans или JAXB для генерации объектов pojo, которые помогут вам маршалировать / unmarshal xml.

http://xmlbeans.apache.org/ https://jaxb.dev.java.net/

Другие вопросы по тегам