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

时间:2012-05-15 09:40:12

标签: java xml filter sax

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

<?xml version="1.0" encoding="UTF-8"?>
<game >
  <moves>
    <turn>2</turn>
    <piece nr="1" />
    <turn>4</turn>
    <piece nr="1" />

  </moves>
</game>

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

  • 转弯元素的内容之和(此处= 6)
  • 片元素的数量(此处= 2)

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

<s:statistics>
    <s:turn-total>6</s:turn-total>
    <s:piece-count>2</s:piece-count>
</s:statistics>

前缀s是对namespace的引用。

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

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

    }

接下来我该怎么做?

3 个答案:

答案 0 :(得分:4)

您的XMLFilter应委托给根据萨克斯事件序列化文档的其他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回调中,您可以检查自己是否处于最终结束标记并添加其他萨克斯事件。

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

答案 1 :(得分:0)

如果我错了,请纠正我,但我认为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;
   }
}

使用Move作为另一个类的实例,该类具有您需要的字段,并且还具有XmlRootElement的注释。

我希望这会有所帮助。

答案 2 :(得分:0)

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

只需创建一个非常基本的ContentHandler并使用它而不是DefaultHandler。在ContentHandler中,您可以实现所有基本功能(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方法中的文件。