Java XML-在新元素内添加现有元素

时间:2020-10-17 10:22:41

标签: java xml xml-parsing

我是Java的新手,正在研究XML文件。跳到这个问题,我必须如下修改给定的XML。

给出XML:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Elements>
    <Item>
        <Element>
            <Value>On</Value>
        </Element>
    </Item>
    <Item>
        <Element>
            <Value>On</Value>
        </Element>
    </Item>
</Elements>

已修改:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Elements>
    <Entry>
        <Item>
            <Element>
                <Value>On</Value>
            </Element>
        </Item>
    </Entry>
    <Entry>
        <Item>
            <Element>
                <Value>On</Value>
            </Element>
        </Item>
    </Entry>
</Elements>

我只需要创建一个新元素“ Entry”并剪切粘贴已经存在的“ Item”。我该如何实现?

3 个答案:

答案 0 :(得分:1)

到目前为止,最简单的方法是使用XSLT样式表。

String xslt = 
 "<Elements xmlns:xsl='http://www.w3.org/1999/XSL/Transform' xsl:version='1.0'>" +
 " <xsl:for-each select='//Item'>" + 
 "  <Entry><xsl:copy-of select='.'/></Entry>" + 
 " </xsl:for-each>" +
 "</Elements>";
TransformerFactory factory = TransformerFactory.newInstance();
Transformer trans = factory.newTransformer(
    new StreamSource(new StringReader(xslt)));
trans.transform(new StreamSource(inputFile), 
                new StreamResult(outputFile)); 

答案 1 :(得分:0)

以下是使用DOM和STAX映射XML的示例。当XML大小较小时,可以使用DOM,它将整个XML加载到内存中并提供对树中每个元素的访问。 STAX是基于事件的解析器,可用于非常大的XML文档。两者都是低级XML处理库。

变量xml是源XML的字符串表示形式,代码中省略了它。

public static void main(String[] args) throws Exception {
        
        // parse and map XML using DOM
        DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        Document doc = db.parse(new InputSource(new StringReader(xml)));
        doc = map(doc);
        System.out.println(serialize(doc));
        
        // parse and map XML using STAX
        InputStream is = new ByteArrayInputStream(xml.getBytes());
        ByteArrayOutputStream os = map(is);
        System.out.println(os.toString(StandardCharsets.UTF_8));
    }

    /**
     * Uses DOM tree, iterating over item elements. 
     * For each item element detaches it, creates new entry element, attaches
     * detached item element to it and inserts entry element into DOM.
     */
    public static Document map(Document doc) {
        NodeList nodes = doc.getDocumentElement().getElementsByTagName("Item");
        for (int i = 0; i < nodes.getLength(); i++) {
            Node child = nodes.item(i);
            child = doc.getDocumentElement().removeChild(child);
            Element entry = doc.createElement("Entry");
            doc.adoptNode(entry);
            doc.getDocumentElement().insertBefore(entry, doc.getDocumentElement().getFirstChild());
            entry.appendChild(child);
        }
        return doc;
    }
    
    /**
     * Uses STaX, event based parser.
     * When handling parser events checks for item start- and end- events and
     * adds appropriate entry element event before or after original event.
     */
    public static ByteArrayOutputStream map(InputStream is) throws Exception {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        XMLEventReader reader = XMLInputFactory.newInstance().createXMLEventReader(is);
        XMLEventWriter writer = XMLOutputFactory.newInstance().createXMLEventWriter(os);
        XMLEventFactory ef = XMLEventFactory.newInstance();
        while(reader.hasNext()) {
            XMLEvent event = reader.nextEvent();
            if(event.isStartElement() && event.asStartElement().getName().getLocalPart().equals("Item")) {
                XMLEvent entryEvent = ef.createStartElement("", null, "Entry");
                writer.add(entryEvent);
            }
            writer.add(event);
            if(event.isEndElement() && event.asEndElement().getName().getLocalPart().equals("Item")) {
                XMLEvent entryEvent = ef.createEndElement("", null, "Entry");
                writer.add(entryEvent);
            }
        }
        return os;
    }

    /**
     * Serializes DOM
     */
    private static String serialize(Document document) {
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        removeWhitespaces(document.getDocumentElement());
        try (StringWriter writer = new StringWriter()) {
            StreamResult result = new StreamResult(writer);
            Transformer transformer = transformerFactory.newTransformer();
            transformer.setOutputProperty(OutputKeys.METHOD, "xml");
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
            transformer.transform(new DOMSource(document), result);
            return writer.toString();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Removes whitespace nodes from DOM
     */
    private static void removeWhitespaces(Element element) {
        NodeList children = element.getChildNodes();
        for (int i = children.getLength() - 1; i >= 0; i--) {
            Node child = children.item(i);
            if (child instanceof Text && ((Text) child).getData().trim().isEmpty()) {
                element.removeChild(child);
            } else if (child instanceof Element) {
                removeWhitespaces((Element) child);
            }
        }
    }

答案 2 :(得分:0)

使用Jackson库,您可以将整个XML有效负载读取为List<Map>并编写自定义序列化程序,它将以新格式进行写入。参见以下示例:

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializable;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;

import javax.xml.namespace.QName;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;

public class XmlMapperApp {

    public static void main(String... args) throws Exception {
        File xmlFile = new File("./resource/test.xml").getAbsoluteFile();

        XmlMapper mapper = XmlMapper.xmlBuilder()
                .enable(SerializationFeature.INDENT_OUTPUT)
                .enable(ToXmlGenerator.Feature.WRITE_XML_DECLARATION)
                .build();

        List<Map<String, Object>> items = readOldXmlFormat(xmlFile, mapper);
        String xml = writeNewXmlFormat(mapper, items);
        System.out.println(xml);
    }

    private static List<Map<String, Object>> readOldXmlFormat(File xmlFile, XmlMapper mapper) throws IOException {
        CollectionType collectionType = mapper.getTypeFactory().constructCollectionType(List.class, Map.class);
        return mapper.readValue(xmlFile, collectionType);
    }

    private static String writeNewXmlFormat(XmlMapper mapper, List<Map<String, Object>> items) throws IOException {
        return mapper.writeValueAsString(new SerializableItems(items));
    }
}

@JacksonXmlRootElement(localName = "Elements")
class SerializableItems implements JsonSerializable {

    private final List<Map<String, Object>> items;

    SerializableItems(List<Map<String, Object>> items) {
        this.items = items;
    }

    private final QName itemQName = new QName("Item");
    private final QName elementQName = new QName("Element");
    private final QName entryQName = new QName("Entry");

    @Override
    public void serialize(JsonGenerator gen, SerializerProvider serializers) throws IOException {
        ToXmlGenerator xmlGen = (ToXmlGenerator) gen;
        xmlGen.writeStartObject();
        for (Map<String, Object> item : items) {
            xmlGen.startWrappedValue(entryQName, itemQName);
            xmlGen.startWrappedValue(itemQName, elementQName);
            for (Map.Entry<String, Object> entry : item.entrySet()) {
                xmlGen.writeObjectField(entry.getKey(), entry.getValue());
            }
            xmlGen.finishWrappedValue(itemQName, elementQName);
            xmlGen.finishWrappedValue(entryQName, itemQName);
        }
        xmlGen.writeEndObject();
    }

    @Override
    public void serializeWithType(JsonGenerator gen, SerializerProvider serializers, TypeSerializer typeSer) throws IOException {
    }
}

上面的代码打印:

<?xml version='1.0' encoding='UTF-8'?>
<Elements>
  <Entry>
    <Item>
      <Element>
        <Value>On</Value>
      </Element>
    </Item>
  </Entry>
  <Entry>
    <Item>
      <Element>
        <Value>On</Value>
      </Element>
    </Item>
  </Entry>
</Elements>
相关问题