2010-03-12 16 views
8

Estoy analizando (muchos) archivos XML que contienen referencias de entidades que no conozco de antemano (no puede cambiar ese hecho).¿Cómo tratar con referencias de entidades desconocidas?

Por ejemplo:

xml = "<tag>I'm content with &funny; &entity; &references;.</tag>" 

cuando trato de analizar esta usando el siguiente código:

final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 
final DocumentBuilder db = dbf.newDocumentBuilder(); 
final InputSource is = new InputSource(new StringReader(xml)); 
final Document d = db.parse(is); 

me sale la siguiente excepción:

org.xml.sax.SAXParseException: The entity "funny" was referenced, but not declared. 

pero, lo que hago Lo que quiero lograr es que el analizador sustituya a todas las entidades que no están declaradas (desconocidas para el analizador) con una cadena vacía. ''. O mejor aún, ¿hay una manera de pasar un mapa para el analizador como:

Map<String,String> entityMapping = ... 
entityMapping.put("funny","very"); 
entityMapping.put("entity","important"); 
entityMapping.put("references","stuff"); 

de modo que pudiera hacer lo siguiente:

final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 
final DocumentBuilder db = dbf.newDocumentBuilder(); 
final InputSource is = new InputSource(new StringReader(xml)); 

db.setEntityResolver(entityMapping); 
final Document d = db.parse(is); 

si iba a obtener el texto del documento utilizando este código de ejemplo que debería recibir:

I'm content with very important stuff. 

¿Alguna sugerencia? Por supuesto, ya estaría feliz de simplemente reemplazar la entidad desconocida con cadenas vacías.

Gracias,

+0

No estoy lo suficientemente familiarizado con el conjunto de herramientas SAX para conocer su API, pero me imagino que tiene un '... Resolver' asociado. Esta clase sería responsable de resolver estas referencias. Así es como funciona el modelo .Net. Creo que los conceptos son en gran medida lo mismo. –

+0

¿Te refieres a EntityResolver? Eso ciertamente parece que debería funcionar, pero cuando miro su API no parece estar dirigido a este tipo de entidad. Sin embargo, intentarlo no debería hacer ningún daño. –

+1

'EntityResolver' es para resolver entidades * externas * (por ejemplo, una DTD), pero estamos buscando algo que maneje entidades * internas *. – skaffman

Respuesta

3

La API StAX tiene soporte para esto. Eche un vistazo a XMLInputFactory, tiene un runtime property que dicta si las entidades internas se expanden o no. Si se establece en false, la secuencia de eventos StAX contendrá instancias de EntityReference para representar las entidades no expandidas.

Si aún desea un DOM como el resultado final, puede encadenar juntos como esto:

XMLInputFactory inputFactory = XMLInputFactory.newInstance(); 
inputFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false); 
Transformer transformer = TransformerFactory.newInstance().newTransformer(); 

String xml = "my xml"; 
StringReader xmlReader = new StringReader(xml); 
XMLEventReader eventReader = inputFactory.createXMLEventReader(xmlReader); 
StAXSource source = new StAXSource(eventReader); 
DOMResult result = new DOMResult(); 

transformer.transform(source, result); 

Node document = result.getNode(); 

En este caso, el DOM resultante contendrá nodos de org.w3c.dom.EntityReference mezclados con los nodos de texto. A continuación, puede procesar estos como mejor le parezca.

3

Desde su entrada XML parece estar disponible como una cadena, no se podía hacer un simple pre-procesamiento con el reemplazo de expresiones regulares?

xml = "..."; 

/* replace entities before parsing */ 
for (Map.Entry<String,String> entry : entityMapping.entrySet()) { 
    xml = xml.replaceAll("&" + entry.getKey() + ";", entry.getValue()); 
} 

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 
... 

Es bastante hacky, y es posible que desee pasar un esfuerzo extra para asegurar que las expresiones regulares único partido en el que realmente debe (piensa <entity name="&don't-match-me;"/>), pero al menos es algo ...

Por supuesto , hay formas más eficientes de lograr el mismo efecto que llamar al replaceAll() muchas veces.

+0

no conozco todas las entidades por adelantado (como ya lo mencioné en mi pregunta) solo conozco un subconjunto. y ¿dónde está el objetivo de usar xml para el que existen analizadores ya maduros cuando termino escribiendo mi propio analizador para poder trabajar con datos xml? – Chris

+2

El punto es este: los analizadores maduros están diseñados para manejar XML bien formado. No tiene XML bien formado y está buscando soluciones para que los analizadores lo manejen de todos modos. –

+0

bueno, podemos discutir mucho, aún tengo este problema por resolver. – Chris

0

Puede agregar las entidades al comienzo del archivo. Mire here para más información.

También puede consultar this thread donde alguien parece haber implementado una interfaz EntityResolver (también podría implementar EntityResolver2!) Donde puede procesar las entidades sobre la marcha (por ejemplo, con su Mapa propuesto).

WARNING: there is a bug! en jdk6, pero se puede tratar con JDK5

+0

no hay un archivo DTD que defina cómo traducir las referencias y tampoco conozco todas las referencias a entidades que pueden aparecer en el documento, así que no puedo crearlo yo mismo. solo conozco un pequeño subconjunto de las referencias que ocurren con frecuencia, pero tampoco puedo decir qué tan "grande" es ese subconjunto. – Chris

+0

bien, pero o bien transforma esas entidades en secciones RAW CDATA o puede 'omitir' o transformar esas entidades sobre la marcha en xml-pure-text o sth. Me gusta esto. De lo contrario, no tendrás la oportunidad. – Karussell

+0

EntityResolver2 -> ver respuesta actualizada – Karussell

Cuestiones relacionadas