使用保留格式的DOM在XML中追加元素

时间:2013-08-23 16:09:31

标签: java xml parsing dom

我有像这样的xml

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Empleado>
  <ConsultorTecnico>
    <Nombre>Pablo</Nombre>
    <Legajo antiguedad="4 meses">7778</Legajo>
  </ConsultorTecnico>
  <CNC>
    <Nombre>Brian</Nombre>
    <Legajo antiguedad="1 año, 7 meses">2134</Legajo>
  <Sueldo>4268.0</Sueldo>
  </CNC>
</Empleado>

我想要的是读取XML并在“CNC”元素中将“Sueldo”添加到与“Nombre”和“Legajo”相同的级别。 “Sueldo”必须是“Legajo”x 2

我在上面的XML中看到的代码我附加了“Sueldo”,但它没有按照它应该缩进它,我使用propierties进行缩进(这个XML是以相同的方式创建的,使用DOM)

public class Main
{
    public static void main(String[] args)
    {
        try
        {
          File xml = new File("C:\\Empleado.xml");
          if (xml.exists() == true)
          {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            Document doc = db.parse(xml);

            String legajo = doc.getElementsByTagName("Legajo").item(1).getFirstChild().getNodeValue();

        Element sueldo = doc.createElement("Sueldo");
        Node valorSueldo = doc.createTextNode(String.valueOf(Float.valueOf(legajo)*2));
        sueldo.appendChild(valorSueldo);

        Node cnc = doc.getElementsByTagName("CNC").item(0);

        cnc.appendChild(sueldo);

        DOMSource source = new DOMSource(doc);
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer t = tf.newTransformer();

        t.setOutputProperty(OutputKeys.INDENT, "yes");
        t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount","2"); 

        FileOutputStream fos = new FileOutputStream("C:\\Empleado.xml");
        StreamResult sr = new StreamResult(fos);

        t.transform(source,sr);    

        }
      else
          throw new Exception("No hay archivo XML con ese nombre en el directorio");
    }
    catch (Exception e) 
    {
      System.out.println(e.getMessage());
    }
}

}

先谢谢你们,我会感谢你们的帮助!

2 个答案:

答案 0 :(得分:1)

假设您的输入文件与您显示但没有Sueldo元素的输出相同,则初始CNC元素具有五个子节点,关注DOM

  • <CNC><Nombre>
  • 之间的空白文字节点(换行符和四个空格)
  • Nombre元素节点
  • </Nombre><Legajo
  • 之间的空白文字节点(换行符和四个空格)
  • Legajo元素节点
  • </Legajo></CNC>
  • 之间的空白文本节点(换行符和两个空格)

您正在此最终文本节点之后插入Sueldo元素,该节点生成

  <CNC>
    <Nombre>Brian</Nombre>
    <Legajo antiguedad="1 año, 7 meses">2134</Legajo>
  <Sueldo>4268.0</Sueldo></CNC>

INDENT输出属性只是将结束</CNC>标记移动到下一行,与开头标记对齐。要使自动缩进执行正确的操作,您需要从初始树中删除 all 仅空白文本节点。

或者,忘记自动缩进并自行完成 - 而不是将Sueldo添加为CNC last 子项(在该最终文本节点之后),而是添加紧跟在Legajo之后(即在最后一个文本节点之前)的换行符和四行空格文本节点,然后在此之后添加Sueldo元素。


作为一种替代方法,我会考虑在XSLT中进行转换而不是使用DOM API

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <!-- ignore whitespace-only text nodes in the input -->
  <xsl:strip-space elements="*"/>
  <!-- and re-indent the output -->
  <xsl:output method="xml" indent="yes" />

  <!-- Copy everything verbatim except where otherwise specified -->
  <xsl:template match="@*|node()">
    <xsl:copy><xsl:apply-templates select="@*|node()" /></xsl:copy>
  </xsl:template>

  <!-- For CNC elements, add a Sueldo as the last child -->
  <xsl:template match="CNC">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()" />
      <Sueldo><xsl:value-of select="Legajo * 2" /></Sueldo>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

您可以使用Java代码中的TransformerFactory API或使用独立的命令行XSLT处理器运行。

答案 1 :(得分:0)

XML本质上没有定义任何缩进或漂亮的形式。如果您希望它“缩进”,则需要使用换行符和空格插入内容。在这种情况下,您需要在元素Legajo之后和元素Sueldo之前的内容。

根据我的口味,最好的策略是忽略来自XML文件的所有格式,并在人类消费之前立即使用广义的漂亮者。或者,更好的是,给他们好的XML编辑器。如果您拥有操作关注此详细信息的XML文件的每个程序,那么XML的大多数好处都会消失(并且会大量滥用)。

更新:注意到您使用元素CNC来“定位”插入内容,而不是Legajo。空格和换行内容需要紧接在元素CNC之前(以及元素Sueldo之后)。