使用SAX Filter将新元素插入XML文件

我有一个XMl文件,看起来像:

   2  4    

我正在编写一个Java程序,它将XML文件作为输入,然后使用SAX和SAXfilter解析它并计算:

  • 转弯元素的总和(此处= 6)
  • 零件数量(此处= 2)

然后我想使用SAXfilter来生成输出XML文件,该输出XML文件与输入XML文件相同,但有一个额外的元素,如:

  6 2  

前缀s是对命名空间的引用。

到目前为止,我的计划是:

  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); } } } 

接下来我该怎么办?

您的XMLFilter应委托给另一个基于sax事件序列化文档的ContentHandler

 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(...)); 

您的回调应该委托给super实现,后者将事件转发给序列化的ContentHandler

 public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { super.startElement(namespaceURI, localName, qName, atts); ... } 

endElement回调中,您可以检查您是否处于最终结束标记并添加其他sax事件。

 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"); } ... } 

如果我错了,请纠正我,但我认为XMLReader和XMLFilter并不是真的应该更改文档。 我可以提供一种不同的方法,您也可以更改文档的内容:

 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(); } 

使用这个类来解析你的xml-Files,你只需要一个像这样的类:

 @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; } } 

with Move是另一个具有所需字段的类的实例,并且还具有XmlRootElement的注释。

我希望这有帮助。

使用@Jorn Horstmann(http://stackoverflow.com/users/139595/jorn-horstmann)上面的答案,您可以轻松添加缺少的元素。 但是要将结果写入XML文件,您应该使用TransformerHandler。

只需创建一个非常基本的ContentHandler并使用它而不是DefaultHandler。 在ContentHandler中,您可以实现所有基本function(startDocument,startElement等)并将每个元素添加到新的Transformer中。 例如,在你的startDocument函数中:

 Transformer t = hd.getTransformer(); t.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); t.setOutputProperty(OutputKeys.METHOD, "xml"); t.setOutputProperty(OutputKeys.INDENT, "yes"); hd.startDocument(); 

然后(在每个其他函数中)添加:例如对于startElement:

 hd.startElement(uri,localName,name,attributes); 

最后,您可以将所有这些写入endDocument方法中的文件。