【问题标题】:Java XML - adding an existing element inside a new elementJava XML - 在新元素中添加现有元素
【发布时间】:2021-01-31 16:44:31
【问题描述】:

我是 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”。我怎样才能实现它?

【问题讨论】:

  • 如果您的输入是一致的,您可能希望将 XML 转换为字符串:stackoverflow.com/questions/315517/…stackoverflow.com/questions/5456680/xml-document-to-string。然后使用 REGEX 选择 &lt;item&gt; 并包装它,在两侧附加 &lt;Entry&gt;
  • 输入和输出都不是格式良好的 XML 文档,这使得这相当棘手。格式良好的文档只有一个最外层元素。
  • @MichaelKay 我刚刚发布了一个示例。将其更新为适当的文档。

标签: java xml xml-parsing


【解决方案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)); 

【讨论】:

    【解决方案2】:

    以下是使用 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);
                }
            }
        }
    

    【讨论】:

      【解决方案3】:

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

      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>
      

      【讨论】:

        猜你喜欢
        • 2017-11-30
        • 1970-01-01
        • 2013-08-16
        • 2014-12-03
        • 2023-03-28
        • 1970-01-01
        • 2014-06-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多