2

XMLの発行に関する以前の質問と同様の状況があります。ストリームにシリアル化しながら、SAXContentHandlerのデータを分析しています。リンクされた質問の解決策は、APIの観点からはまさに私が探しているものですが、XSLTプロセッサを使用したID変換が含まれるため、メモリ効率が良くないのではないかと疑っています。プログラムのメモリ消費量は、入力サイズに応じて大きくなるのではなく、制限されるようにしたいです。

StAXをSAXに適応させるためのアクロバットを行わずに、またはさらに悪いことにSAXイベントの内容を出力ストリームにコピーすることなく、ContentHandlerメソッドのパラメーターをシリアライザーに簡単に転送するにはどうすればよいですか?

編集:これが私が求めているものの最小限の例です。thingIWant与えられたOutputStreamに書き込む必要があります。前に述べたように、前の質問には適切なAPIを提供するTransformerHandlerがありますが、単純なシリアル化ではなくXSLTプロセッサーを使用しています。

public class MyHandler implements ContentHandler {

    ContentHandler thingIWant;

    MyHandler(OutputStream outputStream) {
        thingIWant = setup(outputStream);
    }

    public void startDocument() throws SAXException {
        // parsing logic
        thingIWant.startDocument();
    }

    public void startElement(String uri, String localName, String qName,
                             Attributes atts) throws SAXException {
        // parsing logic
        thingIWant.startElement(uri, localName, qName, atts);
    }

    public void characters(char[] ch, int start, int length) throws SAXException {
        // parsing logic
        thingIWant.characters(ch, start, length);
    }

    // etc...
 }
4

3 に答える 3

4

私は最近、同様の問題を抱えていました。以下は、thingIWant を取得するために作成したクラスです。

import java.io.OutputStream;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerException;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import org.xml.sax.*;

public class XMLSerializer implements ContentHandler {
    static final private TransformerFactory tf = TransformerFactory.newInstance();
    private ContentHandler ch;

    public XMLSerializer(OutputStream os) throws SAXException {
        try {
            final Transformer t = tf.newTransformer();

            t.transform(new SAXSource(                
                new XMLReader() {     
                    public ContentHandler getContentHandler() { return ch; }
                    public DTDHandler getDTDHandler() { return null; }      
                    public EntityResolver getEntityResolver() { return null; }
                    public ErrorHandler getErrorHandler() { return null; }    
                    public boolean getFeature(String name) { return false; }
                    public Object getProperty(String name) { return null; } 
                    public void parse(InputSource input) { }               
                    public void parse(String systemId) { }  
                    public void setContentHandler(ContentHandler handler) { ch = handler; }                
                    public void setDTDHandler(DTDHandler handler) { }
                    public void setEntityResolver(EntityResolver resolver) { }
                    public void setErrorHandler(ErrorHandler handler) { }
                    public void setFeature(String name, boolean value) { }
                    public void setProperty(String name, Object value) { }
                }, new InputSource()),                                    
                new StreamResult(os));
        }
        catch (TransformerException e) {
            throw new SAXException(e);  
        }

        if (ch == null)
            throw new SAXException("Transformer didn't set ContentHandler");
    }

    public void setDocumentLocator(Locator locator) {
        ch.setDocumentLocator(locator);
    }

    public void startDocument() throws SAXException {
        ch.startDocument();
    }

    public void endDocument() throws SAXException {
        ch.endDocument();
    }

    public void startPrefixMapping(String prefix, String uri) throws SAXException {
        ch.startPrefixMapping(prefix, uri);
    }

    public void endPrefixMapping(String prefix) throws SAXException {
        ch.endPrefixMapping(prefix);
    }

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

    public void endElement(String uri, String localName, String qName)
        throws SAXException {
        ch.endElement(uri, localName, qName);
    }

    public void characters(char[] ch, int start, int length)
        throws SAXException {
        this.ch.characters(ch, start, length);
    }

    public void ignorableWhitespace(char[] ch, int start, int length)
        throws SAXException {
        this.ch.ignorableWhitespace(ch, start, length);
    }

    public void processingInstruction(String target, String data)
        throws SAXException {
        ch.processingInstruction(target, data);
    }

    public void skippedEntity(String name) throws SAXException {
        ch.skippedEntity(name);
    }
}

基本的に、Transformer の parse() の呼び出しをインターセプトし、内部の ContentHandler への参照を取得します。その後、クラスは引っかかっている ContentHandler へのプロキシとして機能します。

あまりきれいではありませんが、機能します。

于 2010-02-10T21:09:54.827 に答える
3

まず、恒等変換について心配する必要はありません。データのメモリ内表現は構築されません。

「ティー」機能を実装するには、パーサーによって生成されたイベントのストリームをリッスンし、トランスフォーマーによって提供されたハンドラーにそれらを渡すコンテンツ ハンドラーを作成する必要があります。残念ながら、これは思ったほど簡単ではありません。パーサーはイベントをDefaultHandlerに送信したいのに対し、トランスフォーマーはXMLReaderからイベントを読み込もうとしています。前者は抽象クラス、後者はインターフェースです。JDK は、クラスXMLFilterImplも提供します。このクラスは、 のすべてのインターフェースを実装しますが、それからDefaultHandler拡張することはありません。これは、2 つの異なるプロジェクトを「参照実装」として組み込むことで得られるものです。

したがって、2 つの間のブリッジ クラスを記述する必要があります。

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLFilterImpl;

/**
 *  Uses a decorator ContentHandler to insert a "tee" into a SAX parse/serialize
 *  stream.
 */
public class SaxTeeExample
{
    public static void main(String[] argv)
    throws Exception
    {
        StringReader src = new StringReader("<root><child>text</child></root>");
        StringWriter dst = new StringWriter();

        Transformer xform = TransformerFactory.newInstance().newTransformer();
        XMLReader reader = new MyReader(SAXParserFactory.newInstance().newSAXParser());
        xform.transform(new SAXSource(reader, new InputSource(src)),
                        new StreamResult(dst));

        System.out.println(dst.toString());
    }


    private static class MyReader
    extends XMLFilterImpl
    {
        private SAXParser _parser;

        public MyReader(SAXParser parser)
        {
            _parser = parser;
        }

        @Override
        public void parse(InputSource input) 
        throws SAXException, IOException
        {
            _parser.parse(input, new XMLFilterBridge(this));
        }

        // this is an example of a "tee" function
        @Override
        public void startElement(String uri, String localName, String name, Attributes atts) throws SAXException
        {
            System.out.println("startElement: " + name);
            super.startElement(uri, localName, name, atts);
        }
    }


    private static class XMLFilterBridge
    extends DefaultHandler
    {
        private XMLFilterImpl _filter;

        public XMLFilterBridge(XMLFilterImpl myFilter)
        {
            _filter = myFilter;
        }

        @Override
        public void characters(char[] ch, int start, int length)
        throws SAXException
        {
            _filter.characters(ch, start, length);
        }

        // override all other methods of DefaultHandler
        // ...
    }
}

このmainメソッドは、トランスフォーマーをセットアップします。興味深い部分は、SAXSourceが を中心に構築されていることMyReaderです。トランスフォーマーがイベントの準備ができると、parse()そのオブジェクトのメソッドを呼び出して、指定された を渡しますInputSource

次の部分は明らかではありません: XMLFilterImplDecorator パターンに従います。トランスフォーマーは、変換を開始する前に、このオブジェクトでさまざまなセッター メソッドを呼び出し、独自のハンドラーを渡します。オーバーライドしないメソッド ( などstartDocument()) は、単にデリゲートを呼び出します。オーバーライドの例として、私は "分析" (単なる println) をstartElement(). ContentHandlerおそらく他のメソッドをオーバーライドするでしょう。

最後に、はとXMLFilterBridgeの間の架け橋です。これはデコレーターでもあり、すべてのメソッドはデリゲートを呼び出すだけです。ここでは 1 つのオーバーライドを示していますが、すべてを行う必要があります。DefaultHandlerXmlReader

于 2009-12-30T19:03:21.880 に答える
2

編集:デフォルトのJDKバージョンが含まれています

最も効率的なのは、XMLWriterwhich を実装するContentHandlerです。簡単に言えば、IO バッファとの間で読み書きを行っています。以下で使用されているDOM4JにはXMLWriterがあります。サブクラス化 するか、分析を行うためにXMLWriter使用できます。この例でXMLFilter使用しています。XMLFilterであることに注意してXMLFilterくださいContentHandler。これが完全なコードです。

import org.dom4j.io.XMLWriter;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLFilterImpl;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;
import java.io.PrintStream;

public class XMLPipeline {

    public static void main(String[] args) throws Exception {
        String inputFile = "build.xml";
        PrintStream outputStream = System.out;
        new XMLPipeline().pipe(inputFile, outputStream);
    }

//dom4j
public void pipe(String inputFile, OutputStream outputStream) throws
        SAXException, ParserConfigurationException, IOException {
    XMLWriter xwriter = new XMLWriter(outputStream);
    XMLReader xreader = XMLReaderFactory.createXMLReader();
    XMLAnalyzer analyzer = new XMLAnalyzer(xreader);
    analyzer.setContentHandler(xwriter);
    analyzer.parse(inputFile);

    //do what you want with analyzer
    System.err.println(analyzer.elementCount);
}


//default JDK
public void pipeTrax(String inputFile, OutputStream outputStream) throws
        SAXException, ParserConfigurationException, 
        IOException, TransformerException {
    StreamResult xwriter = new StreamResult(outputStream);
    XMLReader xreader = XMLReaderFactory.createXMLReader();
    XMLAnalyzer analyzer = new XMLAnalyzer(xreader);
    TransformerFactory stf = SAXTransformerFactory.newInstance();
    SAXSource ss = new SAXSource(analyzer, new InputSource(inputFile));
    stf.newTransformer().transform(ss, xwriter);
    System.out.println(analyzer.elementCount);
}

//This method simply reads from a file, runs it through SAX parser and dumps it 
//to dom4j writer
public void dom4jNoop(String inputFile, OutputStream outputStream) throws
        IOException, SAXException {
    XMLWriter xwriter = new XMLWriter(outputStream);
    XMLReader xreader = XMLReaderFactory.createXMLReader();
    xreader.setContentHandler(xwriter);
    xreader.parse(inputFile);

}

//Simplest way to read a file and write it back to an output stream
public void traxNoop(String inputFile, OutputStream outputStream) 
  throws TransformerException {
    TransformerFactory stf = SAXTransformerFactory.newInstance();
    stf.newTransformer().transform(new StreamSource(inputFile), 
     new StreamResult(outputStream));
}    
    //this analyzer counts the number of elements in sax stream
    public static class XMLAnalyzer extends XMLFilterImpl {
        int elementCount = 0;

        public XMLAnalyzer(XMLReader xmlReader) {
            super(xmlReader);
        }

        @Override
        public void startElement(String uri, String localName, String qName, 
          Attributes atts) throws SAXException {
            super.startElement(uri, localName, qName, atts);
            elementCount++;
        }
    }
}
于 2009-12-29T23:30:36.710 に答える