Joh*_*nst 18 java xml xml-parsing
我必须解析Java中的一堆XML文件,有时 - 并且无效地 - 包含HTML实体,例如—,>等等.我理解处理这个问题的正确方法是在解析之前向XML文件添加合适的实体声明.但是,我无法做到这一点,因为我无法控制这些XML文件.
是否存在我可以覆盖的某种回调,只要Java XML解析器遇到这样的实体就会调用它?我无法在API中找到一个.
我想用:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder parser = dbf.newDocumentBuilder();
Document doc = parser.parse( stream );
Run Code Online (Sandbox Code Playgroud)
我发现我可以重写resolveEntity的org.xml.sax.helpers.DefaultHandler,但我要如何使用这与更高级别的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" ));
}
Run Code Online (Sandbox Code Playgroud)
}
使用test.xml:
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<bar>Some text — invalid!</bar>
</foo>
Run Code Online (Sandbox Code Playgroud)
生产:
[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.
Run Code Online (Sandbox Code Playgroud)
更新:我一直在用JDK源代码调试一个调试器和男孩,意味着什么样的意大利面.我不知道那里的设计是什么,或者是否有设计.洋葱的层数可以叠加多少层?
他们的关键类似乎是com.sun.org.apache.xerces.internal.impl.XMLEntityManager,但我找不到任何代码可以让我在使用它之前添加东西,或者尝试解析实体而不通过该类.
为此,我会使用像Jsoup这样的库.我测试了下面的以下内容并且它有效.我不知道这是否有帮助.它可以位于:http://jsoup.org/download
public static void main(String args[]){
String html = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><foo>" +
"<bar>Some text — invalid!</bar></foo>";
Document doc = Jsoup.parse(html, "", Parser.xmlParser());
for (Element e : doc.select("bar")) {
System.out.println(e);
}
}
Run Code Online (Sandbox Code Playgroud)
结果:
<bar>
Some text — invalid!
</bar>
Run Code Online (Sandbox Code Playgroud)
可以在此处找到从文件加载:
http://jsoup.org/cookbook/input/load-document-from-file
问题 - 1:我必须解析 Java中的一堆XML文件,有时 - 并且无效地 - 包含HTML实体,例如
—
XML只有五个预定义的实体.的—, 是不在其中.它仅在纯HTML或旧JSP中使用时才有效.所以,SAX无济于事.它可以使用StaX哪个具有基于高级迭代器的API来完成.(从此链接收集)
问题 - 2:我发现我可以覆盖org.xml.sax.helpers.DefaultHandler中的resolveEntity,但是如何在 更高级别的API中使用它?
用于XML的Streaming API,称为StaX,是一个API reading and writing XML Documents.
StaX是一个Pull-Parsing模型.应用程序可以通过从解析器中提取(获取)事件来控制解析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属性:
需要解析器将内部实体引用替换为替换文本并将其作为字符报告
这可以设置为a XmlInputFactory,然后用于构造XmlEventReader或XmlStreamReader.
但是,API谨慎地说,此属性仅用于强制实现执行替换,而不是强制它替换它们.
你可以尝试一下.希望它能解决你的问题.对于你的情况,
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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<bar>Some text — invalid!</bar>
</foo>
Run Code Online (Sandbox Code Playgroud)
输出:
实体参考:nbsp
实体参考:mdash
信用去@skaffman.
相关链接:
更新:
问题-3:有没有办法使用StaX"过滤"实体(例如用其他东西替换它们)并在流程结束时仍然生成一个文档?
要使用StAX API创建新文档,需要创建一个XMLStreamWriter提供XML开始和结束标记,属性和字符内容的方法.
有5种方法的XMLStreamWriter文档.
xmlsw.writeStartDocument(); - 初始化可以添加元素的空文档xmlsw.writeStartElement(String s) - 创建一个名为s的新元素xmlsw.writeAttribute(String name, String value) - 将属性名称和相应的值添加到对writeStartElement调用生成的最后一个元素.只要没有调用writeElementStart,writeCharacters或writeEndElement,就可以添加属性.xmlsw.writeEndElement - 关闭最后一个启动的元素xmlsw.writeCharacters(String s) - 创建一个新的文本节点,其内容为s作为最后一个启动元素的内容.随附一个示例:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
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 ");
}
}
Run Code Online (Sandbox Code Playgroud)
有关更多信息,请按照教程进行操作
| 归档时间: |
|
| 查看次数: |
8395 次 |
| 最近记录: |