Java XML – 将现有元素添加到新元素内部

huangapple go评论65阅读模式
英文:

Java XML - adding an existing element inside a new element

问题

以下是翻译好的内容:

我刚开始学习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”。我该如何实现这个目标?

英文:

I am new in Java and I am working on parsing a XML file. Jumping to the question, I have to modify the given XML as below.

Given XML:

&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;
&lt;Elements&gt;
    &lt;Item&gt;
	    &lt;Element&gt;
		    &lt;Value&gt;On&lt;/Value&gt;
	    &lt;/Element&gt;
    &lt;/Item&gt;
    &lt;Item&gt;
	    &lt;Element&gt;
		    &lt;Value&gt;On&lt;/Value&gt;
	    &lt;/Element&gt;
    &lt;/Item&gt;
&lt;/Elements&gt;

Modified:

&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;
&lt;Elements&gt;
    &lt;Entry&gt;
        &lt;Item&gt;
	        &lt;Element&gt;
		        &lt;Value&gt;On&lt;/Value&gt;
	        &lt;/Element&gt;
        &lt;/Item&gt;
    &lt;/Entry&gt;
    &lt;Entry&gt;
        &lt;Item&gt;
	        &lt;Element&gt;
		        &lt;Value&gt;On&lt;/Value&gt;
	        &lt;/Element&gt;
        &lt;/Item&gt;
    &lt;/Entry&gt;
&lt;/Elements&gt;

I just have to create a new element 'Entry' and cut paste the already existing 'Item'. How could I achieve it?

答案1

得分: 1

这个任务最简单的方法是使用 XSLT 样式表。

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

By far the easiest way to do this is with an XSLT stylesheet.

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

答案2

得分: 0

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

变量xml是您源XML的字符串表示,在代码中被省略。

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

/**
 * 使用DOM树,迭代item元素。
 * 对于每个item元素,将其分离,创建新的entry元素,将分离的item元素附加到其中,
 * 并将entry元素插入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;
}

/**
 * 使用STaX,基于事件的解析器。
 * 在处理解析器事件时,检查item开始和结束事件,并在原始事件之前或之后添加相应的entry元素事件。
 */
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;
}

/**
 * 序列化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);
    }
}

/**
 * 从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);
        }
    }
}
英文:

Here are examples of mapping XML using DOM and using STAX. DOM can be used when XML size is relatively small, it loads whole XML to memory and provides access to every element of the tree. STAX is event based parser, can be used for very large XML documents. Both are low level XML handling libraries.

Variable xml is string representation of your source XML, it's omitted in the code.

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(&quot;Item&quot;);
for (int i = 0; i &lt; nodes.getLength(); i++) {
Node child = nodes.item(i);
child = doc.getDocumentElement().removeChild(child);
Element entry = doc.createElement(&quot;Entry&quot;);
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() &amp;&amp; event.asStartElement().getName().getLocalPart().equals(&quot;Item&quot;)) {
XMLEvent entryEvent = ef.createStartElement(&quot;&quot;, null, &quot;Entry&quot;);
writer.add(entryEvent);
}
writer.add(event);
if(event.isEndElement() &amp;&amp; event.asEndElement().getName().getLocalPart().equals(&quot;Item&quot;)) {
XMLEvent entryEvent = ef.createEndElement(&quot;&quot;, null, &quot;Entry&quot;);
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, &quot;xml&quot;);
transformer.setOutputProperty(OutputKeys.INDENT, &quot;yes&quot;);
transformer.setOutputProperty(&quot;{http://xml.apache.org/xslt}indent-amount&quot;, &quot;4&quot;);
transformer.setOutputProperty(OutputKeys.ENCODING, &quot;UTF-8&quot;);
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 &gt;= 0; i--) {
Node child = children.item(i);
if (child instanceof Text &amp;&amp; ((Text) child).getData().trim().isEmpty()) {
element.removeChild(child);
} else if (child instanceof Element) {
removeWhitespaces((Element) child);
}
}
}

答案3

得分: 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>
英文:

Using Jackson library you can read whole XML payload as List&lt;Map&gt; and write custom serialiser which will write it in new format. See below example:

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(&quot;./resource/test.xml&quot;).getAbsoluteFile();
XmlMapper mapper = XmlMapper.xmlBuilder()
.enable(SerializationFeature.INDENT_OUTPUT)
.enable(ToXmlGenerator.Feature.WRITE_XML_DECLARATION)
.build();
List&lt;Map&lt;String, Object&gt;&gt; items = readOldXmlFormat(xmlFile, mapper);
String xml = writeNewXmlFormat(mapper, items);
System.out.println(xml);
}
private static List&lt;Map&lt;String, Object&gt;&gt; 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&lt;Map&lt;String, Object&gt;&gt; items) throws IOException {
return mapper.writeValueAsString(new SerializableItems(items));
}
}
@JacksonXmlRootElement(localName = &quot;Elements&quot;)
class SerializableItems implements JsonSerializable {
private final List&lt;Map&lt;String, Object&gt;&gt; items;
SerializableItems(List&lt;Map&lt;String, Object&gt;&gt; items) {
this.items = items;
}
private final QName itemQName = new QName(&quot;Item&quot;);
private final QName elementQName = new QName(&quot;Element&quot;);
private final QName entryQName = new QName(&quot;Entry&quot;);
@Override
public void serialize(JsonGenerator gen, SerializerProvider serializers) throws IOException {
ToXmlGenerator xmlGen = (ToXmlGenerator) gen;
xmlGen.writeStartObject();
for (Map&lt;String, Object&gt; item : items) {
xmlGen.startWrappedValue(entryQName, itemQName);
xmlGen.startWrappedValue(itemQName, elementQName);
for (Map.Entry&lt;String, Object&gt; 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 {
}
}

Above code prints:

&lt;?xml version=&#39;1.0&#39; encoding=&#39;UTF-8&#39;?&gt;
&lt;Elements&gt;
&lt;Entry&gt;
&lt;Item&gt;
&lt;Element&gt;
&lt;Value&gt;On&lt;/Value&gt;
&lt;/Element&gt;
&lt;/Item&gt;
&lt;/Entry&gt;
&lt;Entry&gt;
&lt;Item&gt;
&lt;Element&gt;
&lt;Value&gt;On&lt;/Value&gt;
&lt;/Element&gt;
&lt;/Item&gt;
&lt;/Entry&gt;
&lt;/Elements&gt;

huangapple
  • 本文由 发表于 2020年10月17日 18:22:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/64401385.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定