java - read - xml parser library




Cómo analizar archivos XML no convencionales (2)

Esto es (casi) trivialmente fácil con XSLT:

<xsl:stylesheet 
  version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:ds="http://component.mycompany.com/entity/ds" 
  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://component.mycompany.com/entity/ds" 
>
  <xsl:output indent="yes" />
  <xsl:strip-space elements="*" />

  <xsl:template match="ds:dataView" priority="1">
    <xsl:copy>
      <xsl:apply-templates />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="ds:*[@name]">
    <xsl:element name="{@name}">
      <xsl:apply-templates />
    </xsl:element>
  </xsl:template>

  <xsl:template match="text()">
    <xsl:value-of select="normalize-space()" />
  </xsl:template>

</xsl:stylesheet>

aplicado haga su entrada, le da:

<dataView xmlns="http://component.mycompany.com/entity/ds" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <item1>1</item1>
  <item2>ABC2DEF3</item2>
  <SomeLevel1>
    <SomeLevel2>
      <someItem1>12345</someItem1>
      <someItem2 />
      <someItem3>11:20012:3536</someItem3>
      <someItem4 />
      <someItem5>someItem5 Data</someItem5>
      <someItem6>123456:USD</someItem6>
    </SomeLevel2>
    <SomeLevel3>
      <SomeLevel4>
        <SomeLevel5>
          <someItem6>303.149</someItem6>
          <someItem7>117:USD</someItem7>
          <someItem8>117.000000:USD</someItem8>
          <someItem9>117:USD</someItem9>
          <SomeLevel6 />
        </SomeLevel5>
        <SomeLevel7>
          <someItem10>292.741</someItem10>
          <someItem11>165:USD</someItem11>
          <someItem12>165.000000:USD</someItem12>
          <someItem13>165:USD</someItem13>
        </SomeLevel7>
        <SomeLevel8>
          <someItem14>369.075</someItem14>
          <someItem15>598:USD</someItem15>
          <someItem16>598.000000:USD</someItem16>
          <SomeLevel9>
            <SomeLevel10>
              <someItem17>Some Data | ABC 123</someItem17>
              <someItem18>2</someItem18>
            </SomeLevel10>
          </SomeLevel9>
          <SomeLevel10 />
        </SomeLevel8>
      </SomeLevel4>
    </SomeLevel3>
  </SomeLevel1>
</dataView>

Notas:

Tener nombres de elementos numerados ( someItem1 a someItem6 ) volverá y morderá, eventualmente. Los elementos XML tienen un orden natural (o puede tener un atributo de "número"), poner un contador en el nombre es incorrecto e incorrecto. Sigue mi consejo y no hagas eso (*) .

Para entender la solución aquí hay un desglose rápido:

  • Los procesadores XSLT ejecutan nodos XML a través de plantillas al encontrar la mejor coincidencia para el nodo XML en cuestión.
  • El proceso comienza con el nodo raíz.
  • Como no hay una plantilla que coincida explícitamente con el nodo raíz ( <xsl:template match="/"> ), se aplica una regla predeterminada: no se genera nada, pero se buscan plantillas para los nodos secundarios.
  • El primer (y único) nodo hijo es el elemento de documento ( <ds:dataView> ).
  • La plantilla # 1 coincide con ese nodo, le dice al procesador que la copie y procese sus elementos <xsl:apply-templates /> ( <xsl:apply-templates /> ).
  • Cada elemento que tiene un @name se corresponde con la plantilla # 2, que emite un elemento con ese nombre y nuevamente procesa sus elementos @name .
  • Todos los demás elementos (como esos nodos de <value> ) se procesan, en ausencia de cualquier plantilla coincidente, de acuerdo con la regla predeterminada mencionada.
  • Los nodos de texto se corresponden con la plantilla n. ° 3, que genera su valor recortado.

Notas adicionales:

  • La expresión entre llaves se llama plantilla de valor de atributo .
  • La priority="1" plantilla priority="1" asegura que esta plantilla se seleccione para el elemento <ds:dataView> , porque la otra también coincidirá.
  • Su documento de resultados aún se encuentra en "http://component.mycompany.com/entity/ds" . Esto podría o no ser correcto. Sospecho que no es correcto, técnicamente hablando, pero tú decides.

(*) <xsl:element name="{translate(@name, '0123456789', '')}" sería una variante para perder los contadores.

Tengo la necesidad de tomar lo que considero un archivo XML "no convencional" que obtengo y convertirlo en lo que considero "convencional". Los siguientes ejemplos XML han sido "borrados" de nombres / valores de propiedad.

Aquí se muestra el aspecto del archivo XML "no convencional":

    <?xml version="1.0" encoding="UTF-8"?>
    <dataView name="document/aDocument" xmlns="http://component.mycompany.com/entity/ds" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <dataItems>
        <dataItem name="item1" location="item1" dataItemType="Long">
            <value xsi:type="xs:string">1</value>
        </dataItem>
        <dataItem name="item2" location="item.seconditem(some item).ref" dataItemType="String">
            <value xsi:type="xs:string">ABC2DEF3</value>
        </dataItem>
    </dataItems>
    <dataRepeaters>
        <dataRepeater name="SomeLevel1" runtimeInfomration="SomeLevel11984371030">
            <dataGroups>
                <dataGroup name="SomeLevel2" location="SomeLevel:(a level).thelevel" keyValue="SomeLevel119843710300" runtimeInformation="19843710300">
                    <dataItems>
                        <dataItem name="someItem1" location="someLevel.aLevel(another item)" dataItemType="String">
                            <value xsi:type="xs:string">12345</value>
                        </dataItem>
                        <dataItem name="someItem2" location="someLevel.aLevel(another item2)" dataItemType="Integer"/>
                        <dataItem name="someItem3" location="someLevel.aLevel(another item3)" dataItemType="ObjectReference">
                            <value xsi:type="xs:string">11:20012:3536</value>
                        </dataItem>
                        <dataItem name="someItem4" location="someLevel.aLevel(another item4)" dataItemType="String"/>
                        <dataItem name="someItem5" location="someLevel.aLevel(another item5)" dataItemType="String">
                            <value xsi:type="xs:string">someItem5 Data</value>
                        </dataItem>
                        <dataItem name="someItem6" location="someLevel.aLevel(another item6)" dataItemType="CurrencyAmount">
                            <value xsi:type="xs:string">123456:USD</value>
                        </dataItem>
                    </dataItems>
                </dataGroup>
                <dataGroup name="SomeLevel3" keyValue="SomeLevel31984371030" runtimeInformation="1984371030">
                    <dataRepeaters>
                        <dataRepeater name="SomeLevel4" runtimeInfomration="SomeLevel4">
                            <dataGroups>
                                <dataGroup name="SomeLevel5" location="anotherLevel.level5(SomeLevel5):someLevel5" keyValue="SomeLevel51984371030-11521863690" runtimeInformation="1984371030-11521863690">
                                    <dataItems>
                                        <dataItem name="someItem6" location="someLevel.aLevel(another item6)" dataItemType="BigDecimal">
                                            <value xsi:type="xs:string">303.149</value>
                                        </dataItem>
                                        <dataItem name="someItem7" location="someLevel.aLevel(another item7)" dataItemType="CurrencyAmount">
                                            <value xsi:type="xs:string">117:USD</value>
                                        </dataItem>
                                        <dataItem name="someItem8" location="someLevel.aLevel(another item8)" dataItemType="String">
                                            <value xsi:type="xs:string">117.000000:USD</value>
                                        </dataItem>
                                        <dataItem name="someItem9" location="someLevel.aLevel(another item9)" dataItemType="CurrencyAmount">
                                            <value xsi:type="xs:string">117:USD</value>
                                        </dataItem>
                                    </dataItems>
                                    <dataRepeaters>
                                        <dataRepeater name="SomeLevel6" runtimeInfomration="someLevel6">
                                            <dataGroups/>
                                            </dataRepeater>
                                    </dataRepeaters>
                                </dataGroup>
                                <dataGroup name="SomeLevel7" location="anotherLevel.level5(SomeLevel7):someLevel7" keyValue="SomeLevel71984371030-11521863690" runtimeInformation="1984371030-11521863690">
                                    <dataItems>
                                        <dataItem name="someItem10" location="someLevel.aLevel(another item10)" dataItemType="BigDecimal">
                                            <value xsi:type="xs:string">292.741</value>
                                        </dataItem>
                                        <dataItem name="someItem11" location="someLevel.aLevel(another item11)" dataItemType="CurrencyAmount">
                                            <value xsi:type="xs:string">165:USD</value>
                                        </dataItem>
                                        <dataItem name="someItem12" location="someLevel.aLevel(another item12)" dataItemType="String">
                                            <value xsi:type="xs:string">165.000000:USD</value>
                                        </dataItem>
                                        <dataItem name="someItem13" location="someLevel.aLevel(another item13)" dataItemType="CurrencyAmount">
                                            <value xsi:type="xs:string">165:USD</value>
                                        </dataItem>
                                    </dataItems>
                                    <dataRepeaters/>
                                </dataGroup>
                                <dataGroup name="SomeLevel8" location="anotherLevel.level5(SomeLevel8):someLevel8" keyValue="SomeLevel81984371030-11521863690" runtimeInformation="1984371030-11521863690">
                                    <dataItems>
                                        <dataItem name="someItem14" location="someLevel.aLevel(another item14)" dataItemType="BigDecimal">
                                            <value xsi:type="xs:string">369.075</value>
                                        </dataItem>
                                        <dataItem name="someItem15" location="someLevel.aLevel(another item15)" dataItemType="CurrencyAmount">
                                            <value xsi:type="xs:string">598:USD</value>
                                        </dataItem>
                                        <dataItem name="someItem16" location="someLevel.aLevel(another item16)" dataItemType="String">
                                            <value xsi:type="xs:string">598.000000:USD</value>
                                        </dataItem>
                                    </dataItems>
                                    <dataRepeaters>
                                        <dataRepeater name="SomeLevel9" runtimeInfomration="someLevel9">
                                            <dataGroups>
                                                <dataGroup name="SomeLevel10" location="ownedAuto.AgreementActual(Liability).ConstantRole(Policy Form):policyForm" keyValue="aomeLevel101984371030-11521863690-115218636900" runtimeInformation="1984371030-11521863690-115218636900">
                                                    <dataItems>
                                                        <dataItem name="someItem17" location="someLevel.aLevel(another item17)" dataItemType="String">
                                                            <value xsi:type="xs:string">Some Data | ABC 123</value>
                                                        </dataItem>
                                                        <dataItem name="someItem18" location="someLevel.aLevel(another item18)" dataItemType="Integer">
                                                            <value xsi:type="xs:string">2</value>
                                                        </dataItem>
                                                    </dataItems>
                                                </dataGroup>
                                            </dataGroups>
                                        </dataRepeater>
                                        <dataRepeater name="SomeLevel10" runtimeInfomration="someLevel11">
                                            <dataGroups/>
                                        </dataRepeater>
                                    </dataRepeaters>
                                </dataGroup>
                            </dataGroups>
                        </dataRepeater>
                    </dataRepeaters>
                </dataGroup>
            </dataGroups>
        </dataRepeater>
    </dataRepeaters>
</dataView>

Estoy tratando de convertirlo en algo como esto:

    <?xml version="1.0" encoding="UTF-8"?>
<dataView name="document/aDocument" xmlns="http://component.mycompany.com/entity/ds" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <item1>1</item1>
  <item2>ABC2DEF3</item2>
  <SomeLevel1>
    <SomeLevel2>
      <someItem1>12345f</someItem1>
      <someItem2></someItem2>     
      <someItem3>11:20012:3536</someItem3>
      <someItem4>12345f</someItem4>
      <someItem5>someItem5 Data</someItem5>
      <someItem6>123456:USD</someItem6>
    </SomeLevel2>
    <SomeLevel3>
      <SomeLevel4>
        <SomeLevel5>
          <someItem7>303.149</someItem7>
          <someItem8>117:USD</someItem8>
          <someItem9>117.000000:USD</someItem9>
          <someItem10>117:USD</someItem10>
          <SomeLevel6></SomeLevel6>
        </SomeLevel5>
        <SomeLevel7>
          <someItem11>292.741</someItem11>
          <someItem12>165:USD</someItem12>
          <someItem13>165.000000:USD</someItem13>
          <someItem14>165:USD</someItem14>
        </SomeLevel7>
        <SomeLevel8>
          <someItem15>369.075</someItem15>
          <someItem16>598:USD</someItem16>
          <someItem17>598.000000:USD</someItem17>
          <SomeLevel9>
            <SomeLevel10>
              <someItem18>Some Data | ABC 123</someItem18>
              <someItem19>2</someItem19>
            </SomeLevel10>
          </SomeLevel9>
          <SomeLevel11></SomeLevel11>
        </SomeLevel8>
      </SomeLevel4>
    </SomeLevel3>
  </SomeLevel1>
</dataView>

Así que básicamente estoy tratando de tomar el atributo "nombre" y convertirlo en el nodo / etiqueta XML y el valor entre el nodo / etiqueta <value> y usarlo como el "valor" para el nodo / etiqueta XML.

He intentado varias "rutinas" de análisis / escritura diferentes y las dos siguientes son las dos que me han acercado más. Así que dejo de lado a los otros, ya que serían solo "ruido".

Esto es lo que he intentado hasta ahora:

private Document xmlDocument;

public void convertXML() {
      xmlDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader("DSExample.xml")));

      DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
      DocumentBuilder docBuilder = docFactory.newDocumentBuilder();

      Node dsNode = xmlDocument.getDocumentElement();

      xmlDocument = docBuilder.newDocument();

      // Attempt 1:   
      doSomethingDS(dsNode); // This will convert the XML but with NO nesting - creates example output 1
      // Attempt 2:
      traverseDS(dsNode); // This will create an XML with ONLY the root dataItems - Don't know why - creates example output 2
}

public void doSomethingDS(Node node) {  
    System.out.println(node.getNodeName());
    Element xmlElement = null;

    // This will be the "root" element/node "dataView":
    if(node.getNodeName() != null && node.getNodeName().equalsIgnoreCase("dataView")) {

      // Debugging: Print the node
      printNode(node);

      String nodeName = node.getNodeName().trim();
      rootElement = xmlDocument.createElement(nodeName);
      mPreviousElement = rootElement;

      addAllAttributesToRootElement((Element) node, rootElement);

      xmlDocument.appendChild(rootElement);
    }
    else {

        // Debugging: Print the node
        printNode(node);

        // If has "name" property - create element/node
        if(node.getAttributes() != null && node.getAttributes().getNamedItem("name") != null) {
          xmlElement = createElement(xmlDocument, node);

          if(xmlElement !=null && xmlDocument.getFirstChild() != null) {
            xmlDocument.getFirstChild().appendChild(xmlElement);  
          }
        }

    }    

    NodeList nodeList = node.getChildNodes();
    for(int i = 0; i < nodeList.getLength(); i++) {
      Node currentNode = nodeList.item(i);
      if(currentNode.getNodeType() == Node.ELEMENT_NODE) {
        // recursively call this method for all the children which are of type Element
        doSomethingDS(currentNode);
      }
    }
} // End of doSomethingDS() 


public void traverseDS(Node parentNode) {
    // This will create an XML with ONLY the root dataItems - Don't know why.

    Element xmlElement = null;

    //****************************************************************
    // This will be the "root" element/node "dataView":
    if(parentNode.getNodeName() != null && parentNode.getNodeName().equalsIgnoreCase("dataView")) {

      // Debugging: Print the node
      printNode(parentNode);

      String nodeName = parentNode.getNodeName().trim();
      rootElement = xmlDocument.createElement(nodeName);
      mPreviousElement = rootElement;

      if(!isRootNodeSet) {        
        mRootNode = parentNode;
        isRootNodeSet = true;
      }

      addAllAttributesToRootElement((Element) parentNode, rootElement);

      xmlDocument.appendChild(rootElement);

      // traverse children
      Node theNode = parentNode.getFirstChild();
      if(theNode != null) {
        theNode = theNode.getNextSibling();
      }
      else if (isRootNodeSet) {
        theNode = mRootNode.getNextSibling();
      }

      traverseDS(theNode);
    }
    else {

      // traverse all nodes that belong to the parent
      for(Node theNode = parentNode.getFirstChild(); theNode != null; theNode = theNode.getNextSibling()) {

        // Debugging: Print the node
        printNode(theNode);

        // If has "name" property - create element/node
        if(theNode.getAttributes() != null && theNode.getAttributes().getNamedItem("name") != null) {
          // Create new Element/Node
          xmlElement = createElement(xmlDocument, theNode);
          if(xmlElement !=null && xmlDocument.getFirstChild() != null) {
            xmlDocument.getFirstChild().appendChild(xmlElement);  
          }
          else {
            System.out.println(" not a node we wanted?");
          }
        }

        // traverse children     
        traverseDS(theNode);
      }
    }
} // End of traverseDS()
private Element createElement(Document aDoc, Node aNode) {
    Element xmlElement = null;
    String elementName = "";

    NamedNodeMap dataNodeMap = aNode.getAttributes();
    if(dataNodeMap != null && dataNodeMap.getNamedItem("name") != null) {

      elementName = dataNodeMap.getNamedItem("name").getTextContent();
      xmlElement = aDoc.createElement(elementName);

      // if node = "dataItem" - walk node to get <value> node
      // Note: some "dataItem" nodes also have a <previousValue> node - what to do with these????
      if(aNode.getNodeName() != null && aNode.getNodeName().equalsIgnoreCase("dataItem")) {
        // Walk Node to get <value> node
        NodeList childNodes = aNode.getChildNodes();
        int childNodesLength = childNodes.getLength();
        for(int x = 0; x < childNodesLength; x++) {
          Node childNode = childNodes.item(x);
          if(childNode.getNodeName() != null && childNode.getNodeName().equalsIgnoreCase("value")) {
            xmlElement.setTextContent(childNode.getTextContent());
            break;
          }
        }
      }
    }
    return xmlElement;
} // End of createElement()

Ejemplo de salida 1 (creado por doSomethingDS ()):

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<dataView xmlns="http://component.mycompany.com/entity/ds" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="document/aDocument">
    <item1>1</item1>
    <item2>ABC2DEF3</item2>
    <SomeLevel1/>
    <SomeLevel2/>
    <someItem1>12345</someItem1>
    <someItem2/>
    <someItem3>11:20012:3536</someItem3>
    <someItem4/>
    <someItem5>someItem5 Data</someItem5>
    <someItem6>123456:USD</someItem6>
    <SomeLevel3/>
    <SomeLevel4/>
    <SomeLevel5/>
    <someItem6>303.149</someItem6>
    <someItem7>117:USD</someItem7>
    <someItem8>117.000000:USD</someItem8>
    <someItem9>117:USD</someItem9>
    <SomeLevel6/>
    <SomeLevel7/>
    <someItem10>292.741</someItem10>
    <someItem11>165:USD</someItem11>
    <someItem12>165.000000:USD</someItem12>
    <someItem13>165:USD</someItem13>
    <SomeLevel8/>
    <someItem14>369.075</someItem14>
    <someItem15>598:USD</someItem15>
    <someItem16>598.000000:USD</someItem16>
    <SomeLevel9/>
    <SomeLevel10/>
    <someItem17>Some Data | ABC 123</someItem17>
    <someItem18>2</someItem18>
    <SomeLevel11/>
</dataView>

Ejemplo de salida 2 (Creado por traverseDS ()):

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<dataView xmlns="http://component.mycompany.com/entity/ds" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="document/aDocument">
    <item1>1</item1>
    <item2>ABC2DEF3</item2>
</dataView>

Ahora aquí está la captura ... Lo que obtengo es dinámico. Pude obtener un camión Mac una vez y un Toyota el siguiente o una mezcla de los dos. Y el tamaño del XML que recibo puede ser pequeño o grande. Por lo tanto, es difícil usar XPath o ir directamente a elementos específicos ya que no sé lo que obtendré. Es por eso que básicamente estoy caminando / atravesando el DOM un nodo a la vez para extraer lo que es utilizable.

Entonces mi pregunta es: ¿Qué estoy haciendo mal en mi análisis sintáctico / escritura del archivo XML para que no se vea como lo que intento que se vea?


XSLT se ha creado exactamente para una tarea como esa. Siempre que su Source-XML (xml no convencional) esté bien formado (sintaxis xml correcta) para que pueda ser leído por un analizador xml estándar como DOM, puede usarlo.

Funciona de la siguiente manera: Usted crea un archivo xsl-template que contiene una descripción declerativa de cómo el xml de origen debe transformarse en otro formato. La transformación en sí misma es realizada por un XSL-Transformer. El xsl también es un formato xml, por lo que debe dejar de leerse fácilmente, siempre que la transformación no sea demasiado compleja.

Aquí se explica cómo se puede implementar: http://docs.oracle.com/javase/tutorial/jaxp/xslt/transformingXML.html







xml-parsing