web-dev-qa-db-ja.com

Java内のHTMLエンティティを含むXMLファイルをXMLを変更せずに解析する

私はJavaの一連のXMLファイルを解析する必要があります。これには、—>などのHTMLエンティティが含まれていることがあります。これを処理する正しい方法は、適切なエンティティ宣言を解析前にXMLファイルに追加することですが、これらのXMLファイルを制御できないため、これを行うことはできません。

Java XMLパーサーがこのようなエンティティに遭遇するたびに呼び出される、オーバーライドできるある種のコールバックはありますか?APIでそれを見つけることができませんでした。

使用したい:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

DocumentBuilder parser = dbf.newDocumentBuilder();
Document        doc    = parser.parse( stream );

org.xml.sax.helpers.DefaultHandlerresolveEntityをオーバーライドできることがわかりましたが、これをより高レベルのAPIで使用するにはどうすればよいですか?

ここに完全な例があります:

public class Main {
    public static void main( String [] args ) throws Exception {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder parser = dbf.newDocumentBuilder();
        Document        doc    = parser.parse( new FileInputStream( "test.xml" ));
    }

}

test.xml:

<?xml version="1.0" encoding="UTF-8"?>
<foo>
    <bar>Some&nbsp;text &mdash; invalid!</bar>
</foo>

生成する:

[Fatal Error] :3:20: The entity "nbsp" was referenced, but not declared.
Exception in thread "main" org.xml.sax.SAXParseException; lineNumber: 3; columnNumber: 20; The entity "nbsp" was referenced, but not declared.

更新:私はJDKソースコードをデバッガーといじくり回していました。デザインが何であるか、またはデザインがあるかどうかはわかりません。タマネギの層を何層重ねることができますか?

主要なクラスはcom.Sun.org.Apache.xerces.internal.impl.XMLEntityManagerのようですが、使用前に要素を追加できるコードや、そのクラスを経由せずにエンティティを解決しようとするコードは見つかりません。

19
Johannes Ernst

この目的のために、Jsoupのようなライブラリを使用します。私は以下をテストしましたが、うまくいきます。これが役立つかどうかはわかりません。ここにあります: http://jsoup.org/download

public static void main(String args[]){


    String html = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><foo>" + 
                  "<bar>Some&nbsp;text &mdash; invalid!</bar></foo>";
    Document doc = Jsoup.parse(html, "", Parser.xmlParser());

    for (Element e : doc.select("bar")) {
        System.out.println(e);
    }   


}

結果:

<bar>
 Some&nbsp;text — invalid!
</bar>

ファイルからの読み込みはここにあります:

http://jsoup.org/cookbook/input/load-document-from-file

8
applecrusher

問題-1:私はXMLファイルの束を解析する必要があります Javaそれは時々-そして無効に-_&mdash;_などのHTMLエンティティを含む

XMLには 5つの定義済みエンティティ のみがあります。 _&mdash;_、_&nbsp;_はそれらの中にありません。プレーンHTMLまたはレガシーJSPで使用する場合にのみ機能します。したがって、SAXは役に立ちません。これはStaXを使用して行うことができます高レベルのイテレータベースのAPIを持っています。 (これから収集 リンク

問題-2:org.xml.sax.helpers.DefaultHandlerのresolveEntityをオーバーライドできることがわかりましたが、これを高レベルAPI

StaXと呼ばれるXML用のストリーミングAPIは、_reading and writing XML Documents_のAPIです。

StaXは、プル解析モデルです。アプリケーションは、パーサーからイベントをプル(取得)することにより、XMLドキュメントの解析を制御できます。

コアStaX APIは_two categories_に分類され、以下にリストされています。彼らです

  • カーソルベースのAPI:_low-level API_です。カーソルベースのAPIを使用すると、アプリケーションはXMLをトークン(イベント)のストリームとして処理できます

  • イテレータベースのAPI:_higher-level_イテレータベースのAPIを使用すると、アプリケーションはXMLを一連のイベントオブジェクトとして処理できます。アプリケーションへのXML構造の一部。

_STaX API has support for the notion of not replacing character entity references_、 IS_REPLACING_ENTITY_REFERENCES プロパティを使用:

内部エンティティ参照を置換テキストで置き換え、文字として報告することをパーサーに要求します

これはXmlInputFactoryに設定できます。次に、これを使用してXmlEventReaderまたはXmlStreamReaderを構築します。

ただし、APIは、このプロパティが実装を強制的に置き換えることを意図しているのではなく、強制的に置き換えることを意図したものではないと慎重に言っています。

試してみてください。それがあなたの問題を解決することを願っています。あなたの場合、

Main.Java

_import Java.io.FileInputStream;
import Java.io.FileNotFoundException;

import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.EntityReference;
import javax.xml.stream.events.XMLEvent;

public class Main {

    public static void main(String[] args) {
        XMLInputFactory inputFactory = XMLInputFactory.newInstance();
        inputFactory.setProperty(
                XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false);
        XMLEventReader reader;
        try {
            reader = inputFactory
                    .createXMLEventReader(new FileInputStream("F://test.xml"));
            while (reader.hasNext()) {
                XMLEvent event = reader.nextEvent();
                if (event.isEntityReference()) {
                    EntityReference ref = (EntityReference) event;
                    System.out.println("Entity Reference: " + ref.getName());
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (XMLStreamException e) {
            e.printStackTrace();
        }
    }
}
_

test.xml:

_<?xml version="1.0" encoding="UTF-8"?>
<foo>
    <bar>Some&nbsp;text &mdash; invalid!</bar>
</foo>
_

出力:

エンティティリファレンス:nbsp

エンティティリファレンス:mdash

クレジットは_@skaffman_に送られます。

関連リンク:

  1. http://www.journaldev.com/1191/how-to-read-xml-file-in-Java-using-Java-stax-api
  2. http://www.journaldev.com/1226/Java-stax-cursor-based-api-read-xml-example
  3. http://www.vogella.com/tutorials/JavaXML/article.html
  4. Java文字エンティティを解決せずにドキュメントを解析できるXML APIはありますか?

UPDATE:

問題-3:StaXを使用してエンティティを「フィルタリング」し(たとえば、エンティティを別のものに置き換える)、それでもドキュメントを生成する方法はありますかプロセスの最後に?

StAX APIを使用して新しいドキュメントを作成するには、XMLの開始タグと終了タグ、属性、文字コンテンツを生成するメソッドを提供するXMLStreamWriterを作成する必要があります。

ドキュメントにはXMLStreamWriter5つのメソッドがあります。

  1. xmlsw.writeStartDocument();-要素を追加できる空のドキュメントを初期化します
  2. xmlsw.writeStartElement(String s)-sという名前の新しい要素を作成します
  3. xmlsw.writeAttribute(String name, String value)- writeStartElementの呼び出しによって生成された最後の要素に、対応する値を持つ属性名を追加します。 writeElementStart、writeCharactersまたはwriteEndElementの呼び出しが行われていない限り、属性を追加できます。
  4. _xmlsw.writeEndElement_-最後に開始された要素を閉じる
  5. xmlsw.writeCharacters(String s)-最後に開始された要素のコンテンツとしてコンテンツsを持つ新しいテキストノードを作成します。

サンプルが添付されています。

StAXExpand.Java

_import  Java.io.BufferedReader;
import  Java.io.FileReader;
import  Java.io.IOException;

import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

import Java.util.Arrays;

public class StAXExpand {   
    static XMLStreamWriter xmlsw = null;
    public static void main(String[] argv) {
        try {
            xmlsw = XMLOutputFactory.newInstance()
                          .createXMLStreamWriter(System.out);
            CompactTokenizer tok = new CompactTokenizer(
                          new FileReader(argv[0]));

            String rootName = "dummyRoot";
            // ignore everything preceding the Word before the first "["
            while(!tok.nextToken().equals("[")){
                rootName=tok.getToken();
            }
            // start creating new document
            xmlsw.writeStartDocument();
            ignorableSpacing(0);
            xmlsw.writeStartElement(rootName);
            expand(tok,3);
            ignorableSpacing(0);
            xmlsw.writeEndDocument();

            xmlsw.flush();
            xmlsw.close();
        } catch (XMLStreamException e){
            System.out.println(e.getMessage());
        } catch (IOException ex) {
            System.out.println("IOException"+ex);
            ex.printStackTrace();
        }
    }

    public static void expand(CompactTokenizer tok, int indent) 
        throws IOException,XMLStreamException {
        tok.skip("["); 
        while(tok.getToken().equals("@")) {// add attributes
            String attName = tok.nextToken();
            tok.nextToken();
            xmlsw.writeAttribute(attName,tok.skip("["));
            tok.nextToken();
            tok.skip("]");
        }
        boolean lastWasElement=true; // for controlling the output of newlines 
        while(!tok.getToken().equals("]")){ // process content 
            String s = tok.getToken().trim();
            tok.nextToken();
            if(tok.getToken().equals("[")){
                if(lastWasElement)ignorableSpacing(indent);
                xmlsw.writeStartElement(s);
                expand(tok,indent+3);
                lastWasElement=true;
            } else {
                xmlsw.writeCharacters(s);
                lastWasElement=false;
            }
        }
        tok.skip("]");
        if(lastWasElement)ignorableSpacing(indent-3);
        xmlsw.writeEndElement();
   }

    private static char[] blanks = "\n".toCharArray();
    private static void ignorableSpacing(int nb) 
        throws XMLStreamException {
        if(nb>blanks.length){// extend the length of space array 
            blanks = new char[nb+1];
            blanks[0]='\n';
            Arrays.fill(blanks,1,blanks.length,' ');
        }
        xmlsw.writeCharacters(blanks, 0, nb+1);
    }

}
_

CompactTokenizer.Java

_import  Java.io.Reader;
import  Java.io.IOException;
import  Java.io.StreamTokenizer;

public class CompactTokenizer {
    private StreamTokenizer st;

    CompactTokenizer(Reader r){
        st = new StreamTokenizer(r);
        st.resetSyntax(); // remove parsing of numbers...
        st.wordChars('\u0000','\u00FF'); // everything is part of a Word
                                         // except the following...
        st.ordinaryChar('\n');
        st.ordinaryChar('[');
        st.ordinaryChar(']');
        st.ordinaryChar('@');
    }

    public String nextToken() throws IOException{
        st.nextToken();
        while(st.ttype=='\n'|| 
              (st.ttype==StreamTokenizer.TT_Word && 
               st.sval.trim().length()==0))
            st.nextToken();
        return getToken();
    }

    public String getToken(){
        return (st.ttype == StreamTokenizer.TT_Word) ? st.sval : (""+(char)st.ttype);
    }

    public String skip(String sym) throws IOException {
        if(getToken().equals(sym))
            return nextToken();
        else
            throw new IllegalArgumentException("skip: "+sym+" expected but"+ 
                                               sym +" found ");
    }
}
_

詳細については、チュートリアルに従うことができます

  1. https://docs.Oracle.com/javase/tutorial/jaxp/stax/example.html
  2. http://www.ibm.com/developerworks/library/x-tipstx2/index.html
  3. http://www.iro.umontreal.ca/~lapalme/ForestInsteadOfTheTrees/HTML/ch09s03.html
  4. http://staf.sourceforge.net/current/STAXDoc.pdf
6
SkyWalker

とにかく厳格なOXMアプローチを使用していないため、別のアプローチ。 JSoupなどの厳格でないパーサーを使用してみませんか?これにより、無効なXMLスキーマなどの問題がすぐになくなりますが、問題がコードに展開されるだけです。

3
Richard

ソリューションへの異なるアプローチを投入するだけです。

入力ストリームを、エンティティを正当なもので置き換えるストリーム補完で包むことができます。

これは確かにハックですが、迅速かつ簡単な解決策である必要があります(またはより適切に言うと、回避策)。
ただし、xmlフレームワークの内部ソリューションほどエレガントでクリーンではありません。

1
rpy

私は昨日、ストリーム内の解凍されたXMLからデータベースに値を追加する必要がある同様の何かを作りました。

//import I'm not sure if all are necessary :) 
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.*;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

//I didnt checked this code now because i'm in work for sure its work maybe 
you will need to do little changes
InputSource is = new InputSource(new FileInputStream("test.xml"));

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(is);
XPathFactory xpf = XPathFactory.newInstance();
XPath xpath = xpf.newXPath();
String words= xpath.evaluate("/foo/bar", doc.getDocumentElement());
ParsingHexToChar.parseToChar(words);

// lib which i use common-lang3.jar
//metod to parse 
public static String parseToChar( String words){

    String decode= org.Apache.commons.lang3.StringEscapeUtils.unescapeHtml4(words);

        return decode;
 }
1

Org.Apache.commonsパッケージを使用してこれを試してください:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder parser = dbf.newDocumentBuilder();

InputStream in = new FileInputStream(xmlfile);    
String unescapeHtml4 = IOUtils.toString(in);

CharSequenceTranslator obj = new AggregateTranslator(new LookupTranslator(EntityArrays.ISO8859_1_UNESCAPE()),
          new LookupTranslator(EntityArrays.HTML40_EXTENDED_UNESCAPE())    
         );

unescapeHtml4 = obj.translate(unescapeHtml4);
StringReader readerInput= new StringReader(unescapeHtml4);

InputSource is = new InputSource(readerInput);
Document doc    = parser.parse(is);    
0
V_Dev