java - studio - Inserte un nuevo elemento en un archivo XML usando SAX Filter
manejo de archivos xml en java (3)
Corrígeme si estoy equivocado, pero creo que XMLReader y XMLFilter realmente no deben cambiar un documento. Puedo proporcionar un enfoque diferente que con usted puede cambiar el contenido de su documento también:
public class ExtXMLConfig {
private JAXBContext context;
private Marshaller m;
private Unmarshaller um;
private Schema schema = null;
/**
* Creates an ExtXMLConfig-object, which uses rootClass as object to parse
* and save XML-files.
*
* @param rootClass
* the class use create/parse xml-files from
* @throws JAXBException
*/
public ExtXMLConfig(Class<?> rootClass) throws JAXBException {
context = JAXBContext.newInstance(rootClass);
init();
}
/**
* Creates an ExtXMLConfig, which uses a classPath like javax.xml.bin to use
* all classes in that path to parse and write xml-files
*
* @param classPath
* the class path containing all needed java-objects
* @throws JAXBException
*/
public ExtXMLConfig(String classPath) throws JAXBException {
context = JAXBContext.newInstance(classPath);
init();
}
/**
* Parses a xml-file into a JavaObject.
*
* @param file
* path to the xml-file
* @return a java-Object
*/
public Object load(String file) {
return load(new File(file));
}
/**
* Parses a xml-file into a JavaObject.
*
* @param xml
* File-object representing the xml-file
* @return a java-Object
*/
public Object load(File xml) {
um.setSchema(schema);
if (xml.exists() && xml.isFile()) {
try {
return um.unmarshal(xml);
} catch (JAXBException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
System.out.println("Failed to open file: " + xml.getAbsolutePath());
}
return null;
}
/**
* Saves a object into a xml-file.
*
* @param xml
* the object to save
* @param file
* path to the file to save to
*/
public void save(Object xml, String file) {
save(xml, new File(file));
}
/**
* Saves a object into a xml-file.
*
* @param xml
* the object to save
* @param file
* File-object representing the file to save to
*/
public void save(Object xml, File file) {
if (xml != null) {
m.setSchema(schema);
if (!file.isDirectory()) {
try {
if (!file.exists()) {
file.createNewFile();
}
m.marshal(xml, file);
} catch (JAXBException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
/**
* Returns a formatted string representation of a xml-file given as a
* java-Object.
*
* @param xml
* the java-object to parse the xml from.
* @return a formatted string representation of the given object
*/
public String toString(Object xml) {
StringWriter out = new StringWriter();
try {
m.setSchema(schema);
m.marshal(xml, out);
return out.toString();
} catch (JAXBException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
private void init() throws JAXBException {
m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
um = context.createUnmarshaller();
}
Usando esta clase para analizar tus archivos xml, solo necesitarías una clase como esta:
@XmlRootElement // used to parse this class as xml-Root
public class Game {
private Move moves;
public Game() {};
public void setMove(Move moves) {
this.moves = moves;
}
public Moves getMoves() {
return this.moves;
}
}
con Move siendo una instancia de otra clase que tiene los campos que necesita y también tiene una anotación para XmlRootElement.
Espero que esto ayude.
Tengo un archivo XMl que se ve así:
<?xml version="1.0" encoding="UTF-8"?>
<game >
<moves>
<turn>2</turn>
<piece nr="1" />
<turn>4</turn>
<piece nr="1" />
</moves>
</game>
Estoy escribiendo un programa Java que toma el archivo XML como entrada y luego lo analiza con SAX y filtro SAX y calcula:
- la suma del contenido del elemento de giro (aquí = 6)
- la cantidad de elementos pieza (aquí = 2)
Luego quiero usar un filtro SAX para generar un archivo XML de salida que sea el mismo que el de entrada, pero con un elemento adicional como:
<s:statistics>
<s:turn-total>6</s:turn-total>
<s:piece-count>2</s:piece-count>
</s:statistics>
El prefijo s
es una referencia a un espacio de nombres .
Mi programa hasta ahora es:
public class test{
public static void main(String[] args) throws Exception {
if (args.length != 2) {
System.err.println("error ");
System.exit(1);
}
String xmlInput = args[0];
String filteredXML = args[1];
test test1 = new test();
test1.sax(xmlInput, filteredXML);
}
private void sax(String gameXML, String filteredGameXML)throws Exception{
FileInputStream fis = new FileInputStream( gameXML);
InputSource is = new InputSource(fis);
XMLReader xr = XMLReaderFactory.createXMLReader();
XMLFilter xf = new MyFilter();
xf.setParent(xr);
xr = xf;
xr.parse(is);
xr.setFeature("http://xml.org/sax/features/namespaces", true);
DefaultHandler handler = new DefaultHandler();
xr.setContentHandler(handler);
}
private class MyFilter extends XMLFilterImpl{
StringBuffer buffer;
int temp=0;
int sum=0;
String ff;
int numof=0;
private MyFilter() {}
@Override
public void startDocument() throws SAXException {
System.out.println( "START DOCUMENT" );
numof=0;
}
public void startElement(String namespaceURI, String localName, String name, Attributes attributes) throws SAXException{
if(localName.equals("turn")){
buffer=new StringBuffer();
}
if("piece".equals(name)){
numof++;
}
}
public void characters(char[] ch, int start, int length) throws SAXException {
String s=new String(ch, start, length);
if(buffer!=null){
buffer.append(s);
}
}
public void endElement(String uri, String localName, String name)throws SAXException {
if(buffer!=null ){
ff=buffer.toString();
temp=Integer.valueOf(ff);
sum=sum+temp;
}
buffer=null;
}
public void endDocument() throws SAXException {
System.out.println( "END DOCUMENT" );
System.out.println("sum of turn: "+ sum);
System.out.println("sum of piece: "+ numof);
}
}
}
¿Qué debería hacer después?
Utilizando la respuesta de @Jorn Horstmann (http://.com/users/139595/jorn-horstmann) desde arriba, puede agregar fácilmente los elementos faltantes. Pero para escribir los resultados en un archivo XML, debe usar el TransformerHandler.
Simplemente cree un ContentHandler muy básico y úselo en lugar del DefaultHandler. En ContentHandler puede implementar todas las funciones básicas (startDocument, startElement, etc.) y agregar cada elemento a un nuevo Transformer. Por ejemplo, en su función startDocument:
Transformer t = hd.getTransformer();
t.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
t.setOutputProperty(OutputKeys.METHOD, "xml");
t.setOutputProperty(OutputKeys.INDENT, "yes");
hd.startDocument();
Y luego (en cada otra función) agregue esto: por ejemplo, para startElement:
hd.startElement(uri,localName,name,attributes);
Finalmente puede escribir todo esto en un archivo en el método endDocument.
Su XMLFilter
debe delegar en otro ContentHandler
que ContentHandler
el documento basado en los eventos sax.
SAXTransformerFactory factory = (SAXTransformerFactory)TransformerFactory.newInstance();
TransformerHandler serializer = factory.newTransformerHandler();
Result result = new StreamResult(...);
serializer.setResult(result);
XMLFilterImpl filter = new MyFilter();
filter.setContentHandler(serializer);
XMLReader xmlreader = XMLReaderFactory.createXMLReader();
xmlreader.setFeature("http://xml.org/sax/features/namespaces", true);
xmlreader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
xmlreader.setContentHandler(filter);
xmlreader.parse(new InputSource(...));
Su devolución de llamada debe delegar en la implementación super
, que reenvía los eventos al serializador ContentHandler
.
public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
super.startElement(namespaceURI, localName, qName, atts);
...
}
En su endElement
llamada endElement
puede verificar si se encuentra en la etiqueta de cierre final y agregar eventos sax adicionales.
public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
super.endElement(namespaceURI, localName, qName);
if ("game".equals(localName)) {
super.startElement("", "statistics", "statistics", new AttributesImpl());
char[] chars = String.valueOf(num).toCharArray();
super.characters(chars, 0, chars.length);
super.endElement("", "statistics", "statistics");
}
...
}