2012-05-18 10 views
6

Estoy trabajando en una aplicación de Android que interactúa con una cámara bluetooth. Para cada clip almacenado en la cámara almacenamos algunos campos sobre el clip (algunos de los cuales el usuario puede cambiar) en un archivo XML.Cómo conservar nodos XML que no están vinculados a un objeto cuando se utiliza SAX para el análisis

Actualmente, esta aplicación es la única que escribe estos datos xml en el dispositivo, pero en el futuro es posible que una aplicación de escritorio o una aplicación de iPhone también pueda escribir datos aquí. No quiero suponer que otra aplicación tampoco podría tener campos adicionales (especialmente si tenían una versión más nueva de la aplicación que agregaba nuevos campos que esta versión aún no era compatible).

Lo que quiero evitar es una situación en la que agregamos nuevos campos a este archivo XML en otra aplicación, y luego el usuario va a usar la aplicación de Android y borra esos otros campos porque no conoce ellos.

por lo que permite tomar ejemplo hipotético:

<data> 
    <title>My Title</title> 
    <date>12/24/2012</date> 
    <category>Blah</category> 
</data> 

cuando se lee desde el dispositivo de esto traduce a un objeto de clip que tiene este aspecto (simplificado por razones de brevedad)

public class Clip { 
    public String title, category; 
    public Date date; 
} 

Así que' m usando SAX para analizar los datos y almacenarlos en un Clip. Simplemente almaceno los caracteres en StringBuilder y los escribo cuando llego al elemento final para título, categoría y fecha.

Me di cuenta de que cuando escribiera estos datos de nuevo en el dispositivo, si hubiera otras etiquetas en el documento original, no se escribirían porque solo escribo los campos que conozco.

Esto me hace pensar que tal vez SAX es la opción incorrecta y tal vez debería usar DOM o alguna otra cosa donde podría escribir más fácilmente cualquier otro elemento que existiera originalmente.

Ocasionalmente estaba pensando que tal vez mi clase Clip contiene una ArrayList de algún tipo XML genérico (quizás DOM), y en startTag compruebo si el elemento no es una de las etiquetas predefinidas, y si es así, hasta que llego al final de esa etiqueta, almaceno toda la estructura (¿pero en qué?). Luego, al escribir de nuevo, revisaría todas las etiquetas adicionales y las escribiría en el archivo xml (junto con los campos que conozco, por supuesto)

¿Es este un problema común con una buena solución conocida?

- Actualización 5/22/12 -

No he mencionado que en el XML real del nodo raíz (en realidad se llama anotación), se utiliza un número de versión que ha sido puesto a 1. Lo Lo que haré a corto plazo es requerir que el número de versión que admite mi aplicación sea> = cuál es el número de versión de los datos xml. Si el xml es un número mayor, intentaré analizarlo para su posterior lectura, pero denegaré cualquier guarda en el modelo. Todavía estoy interesado en cualquier tipo de ejemplo de trabajo sobre cómo hacer esto.

BTW Pensé en otra solución que debería ser bastante fácil. Me imagino que puedo usar XPATH para encontrar nodos que conozco y reemplazar el contenido de esos nodos cuando se actualicen los datos. Sin embargo, ejecuté algunos puntos de referencia y la sobrecarga es absurda al analizar el xml cuando se analiza en la memoria. Solo la operación de análisis sin siquiera hacer ninguna búsqueda resultó en un rendimiento 20 veces peor que SAX. El uso de xpath fue entre 30 y 50 veces más lento en general para el análisis sintáctico, lo que fue realmente malo considerando que los analicé en una vista de lista. Así que mi idea es mantener el SAX para analizar los nodos a los clips, pero almacenar la totalidad del XML en una variable de la clase Clip (recuerde, este xml es corto, menos de 2kb). Luego, cuando vaya a volver a escribir los datos, podría usar XPATH para reemplazar los nodos que conozco en el XML original.

Sin embargo, todavía estoy interesado en otras soluciones. Probablemente no acepte una solución, a menos que incluya algunos ejemplos de código.

+0

Actualicé mi respuesta con la implementación que se mantiene con el modelo SAX y utiliza 'XMLFilter's. Agrega una pequeña sobrecarga de mantener la grabación de los eventos en memoria (aunque el modelo de objeto es muy simple). ver si te gusta esa –

+0

¡Muy buena solución! No creo que los gastos generales consideren mucho, en la mayoría de los casos, no habrá ningún nodo adicional. –

Respuesta

1

Así es como se puede ir sobre ella con SAX filters:

  1. Al leer el documento con Sax podrá récord todos los eventos. Usted los graba y los inflama más al siguiente nivel del lector SAX. Básicamente, apila juntas dos capas de lectores SAX (con XMLFilter): una registrará y retransmitirá, y la otra es su controlador SAX actual que crea objetos.
  2. Cuando esté listo para volver a escribir sus modificaciones en el disco, iniciará los eventos SAX grabados en capas con su escritor que sobrescribirían los valores/nodos que ha modificado.

Pasé un tiempo con la idea y funcionó. Básicamente se redujo al encadenamiento correcto de XMLFilter s.Así es como the unit test parece, el código podría hacer algo similar:

final SAXParserFactory factory = SAXParserFactory.newInstance(); 
final SAXParser parser = factory.newSAXParser(); 

final RecorderProxy recorder = new RecorderProxy(parser.getXMLReader()); 
final ClipHolder clipHolder = new ClipHolder(recorder); 

clipHolder.parse(new InputSource(new StringReader(srcXml))); 

assertTrue(recorder.hasRecordingToReplay()); 

final Clip clip = clipHolder.getClip(); 
assertNotNull(clip); 
assertEquals(clip.title, "My Title"); 
assertEquals(clip.category, "Blah!"); 
assertEquals(clip.date, Clip.DATE_FORMAT.parse("12/24/2012")); 

clip.title = "My Title Updated"; 
clip.category = "Something else"; 

final ClipSerializer serializer = new ClipSerializer(recorder); 
serializer.setClip(clip); 

final TransformerFactory xsltFactory = TransformerFactory.newInstance(); 
final Transformer t = xsltFactory.newTransformer(); 
final StringWriter outXmlBuffer = new StringWriter(); 

t.transform(new SAXSource(serializer, 
      new InputSource()), new StreamResult(outXmlBuffer)); 

assertEquals(targetXml, outXmlBuffer.getBuffer().toString()); 

Las líneas importantes son:

  • su SAX events recorder se envuelve alrededor del analizador SAX
  • su analizador Clip (ClipHolder) está envuelto alrededor de la grabadora
  • cuando se analiza el XML, la grabadora registrará todo y su ClipHolder solo mirará lo que sabe acerca de
  • , a continuación, hacer lo que tiene que ver con the clip object
  • la serializer se envuelve alrededor de la grabadora (básicamente re-mapeo que sobre sí misma)
  • que luego trabajar con el serializador y se hará cargo de la alimentación de la eventos grabados (delegando al principal y registrando self como ContentHandler) superpuestos con lo que tiene que decir sobre el objeto clip.

Por favor, encontrar el código de DVR y la prueba de Clipover at github. Espero que ayude.

p.s. no es una solución genérica y todo el concepto de grabación -> repetición + superposición es muy rudimentario en la implementación provista. Una ilustración básicamente. Si su XML es más complejo y se pone "peludo" (por ejemplo, los mismos nombres de elemento en diferentes niveles, etc.), entonces la lógica tendrá que ser aumentada. El concepto seguirá siendo el mismo sin embargo.

0

Si no está limitado a un esquema XML específico, usted debe considerar hacer algo como esto:

<data> 
    <element id="title"> 
     myTitle 
    </element> 
    <element id="date"> 
     18/05/2012 
    </element> 
    ... 
</data> 

y luego almacenar todos esos elementos en un solo ArrayList. De esta forma no perderá información, y aún tiene la posibilidad de elegir qué elemento desea mostrar-editar-etc ...

+0

esto no es posible ya que la estructura actual ya está siendo analizada por el servidor y otra aplicación de escritorio. Además, muchos dispositivos ya tienen la estructura actual en ellos. Además, los nodos adicionales que podrían estar en el xml en el futuro pueden no ser simples valores de texto, pueden tener nodos dentro de ellos. –

1

Tiene razón al decir que SAX probablemente no sea la mejor opción si quieres mantener los nodos que no has "consumido". Todavía podrías hacerlo usando algún tipo de "sax store" que mantendría los eventos SAX y reproducirlos (hay algunas implementaciones similares), pero una API basada en un modelo de objeto sería mucho más fácil de usar: tu d mantenga fácilmente el modelo completo de objetos y simplemente actualice "sus" nodos.

Por supuesto, puede usar DOM que es el estándar, pero también puede considerar alternativas que brinden un acceso más fácil a los nodos específicos que utilizará en un modelo de datos arbitrario. Entre ellos, JDOM (http://www.jdom.org/) y XOM (http://www.xom.nu/) son candidatos interesantes.

0

Su suposición de que XPath es 20 veces más lento que el análisis SAX está dañado ... El análisis SAX es solo un tokenizador de bajo nivel en el que se generaría su lógica de procesamiento ... y su lógica de procesamiento requeriría un análisis adicional ... XPath el rendimiento tiene mucho que ver con la implementación ... Hasta donde yo sé, el XPath de vtd-xml es al menos un orden de magnitud más rápido que el DOM en general, y es mucho más adecuado para el procesamiento XML de alto rendimiento ... a continuación se muestran algunos enlaces a otras referencias ...

http://sdiwc.us/digitlib/journal_paper.php?paper=00000582.pdf

Android - XPath evaluate very slow

+0

Es muy posible que yo no esté usando xpath de manera eficiente o que podría haber usado bibliotecas alternativas que hubieran sido más eficientes. Gracias por la respuesta. Buscaré en vtd-xml si vuelvo a trabajar en esa área del proyecto pronto. –

Cuestiones relacionadas