Scala/Java不尊重w3吗;过多的dtd流量“;规格?
我是Scala的新手,所以我可能不太了解这一点,我想知道问题是否出在我的代码上。给定Scala文件httpparse,简化为:Scala/Java不尊重w3吗;过多的dtd流量“;规格?,java,xml,scala,w3c,dtd,Java,Xml,Scala,W3c,Dtd,我是Scala的新手,所以我可能不太了解这一点,我想知道问题是否出在我的代码上。给定Scala文件httpparse,简化为: object Http { import java.io.InputStream; import java.net.URL; def request(urlString:String): (Boolean, InputStream) = try { val url = new URL(urlString)
object Http {
import java.io.InputStream;
import java.net.URL;
def request(urlString:String): (Boolean, InputStream) =
try {
val url = new URL(urlString)
val body = url.openStream
(true, body)
}
catch {
case ex:Exception => (false, null)
}
}
object HTTPParse extends Application {
import scala.xml._;
import java.net._;
def fetchAndParseURL(URL:String) = {
val (true, body) = Http request(URL)
val xml = XML.load(body) // <-- Error happens here in .load() method
"True"
}
}
结果总是:
java.io.IOException: Server returned HTTP response code: 503 for URL: http://www.w3.org/TR/html4/strict.dtd
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1187)
at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(XMLEntityManager.java:973)
at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startEntity(XMLEnti...
我已经看到了关于Java的讨论,以及关于不尝试通过web访问此DTD的讨论。我还将错误隔离到XML.load()方法,据我所知,这是一个Scala库方法
我的问题:如何解决这个问题?这是我的代码(抄袭而来)的副产品,是我需要解决的特定于Java的问题(如中所述)的副产品,还是特定于Scala的问题?这个调用发生在哪里,它是一个bug还是一个特性?(“是我吗?是她,对吗?”)您试图做的有两个问题:
- Scala的xml解析器正在尝试物理检索DTD,而实际上它不应该检索DTD。J-16 SDiZ似乎对这个问题有一些建议
- 您试图解析的堆栈溢出页不是XML。它是严格的Html4
您必须使用XML解析器以外的工具解析页面,或者研究使用tidy之类的实用程序将html转换为XML。这是scala的问题。本机Java有一个选项可以禁用加载DTD:
f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
scala中没有等价物
如果您想自己修复它,请选中scala/xml/parsing/FactoryAdapter.scala
,并将该行输入
278 def loadXML(source: InputSource): Node = {
279 // create parser
280 val parser: SAXParser = try {
281 val f = SAXParserFactory.newInstance()
282 f.setNamespaceAware(false)
我对Scala的了解很差,但你不能改用它吗
我遇到了同样的问题,我还没有找到一个优雅的解决方案(我正在考虑将这个问题发布到Scala邮件列表中),同时,我找到了一个解决方法:实现您自己的SAXParserFactoryImpl,这样您就可以设置f.setFeature(“,true”);财产。好的方面是它不需要对Scala代码库进行任何代码更改(不过我同意它应该是固定的)。 首先,我扩展默认的解析器工厂:
package mypackage;
public class MyXMLParserFactory extends SAXParserFactoryImpl {
public MyXMLParserFactory() throws SAXNotRecognizedException, SAXNotSupportedException, ParserConfigurationException {
super();
super.setFeature("http://xml.org/sax/features/validation", false);
super.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false);
super.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
super.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
}
}
没什么特别的,我只是想有机会设置属性
(注意:这是纯Java代码,很可能您也可以在Scala中编写同样的代码)
在Scala代码中,您需要配置JVM以使用新工厂:
System.setProperty("javax.xml.parsers.SAXParserFactory", "mypackage.MyXMLParserFactory");
然后可以调用XML.load而无需验证而无需解决问题,目前,如果下面的函数请求返回false,您希望发生什么
def fetchAndParseURL(URL:String) = {
val (true, body) = Http request(URL)
将发生的情况是,将抛出一个异常。不过,您可以这样重写:
def fetchAndParseURL(URL:String) = (Http request(URL)) match {
case (true, body) =>
val xml = XML.load(body)
"True"
case _ => "False"
}
现在,为了解决XML解析问题,我们将禁用解析器中的DTD加载,正如其他人所建议的:
def fetchAndParseURL(URL:String) = (Http request(URL)) match {
case (true, body) =>
val f = javax.xml.parsers.SAXParserFactory.newInstance()
f.setNamespaceAware(false)
f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
val MyXML = XML.withSAXParser(f.newSAXParser())
val xml = MyXML.load(body)
"True"
case _ => "False"
}
现在,我将MyXML内容放在fetchAndParseURL中只是为了尽可能保持示例的结构不变。对于实际使用,我会将其分离到顶级对象中,并将“parser”转换为def而不是val,以避免可变解析器出现问题:
import scala.xml.Elem
import scala.xml.factory.XMLLoader
import javax.xml.parsers.SAXParser
object MyXML extends XMLLoader[Elem] {
override def parser: SAXParser = {
val f = javax.xml.parsers.SAXParserFactory.newInstance()
f.setNamespaceAware(false)
f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
f.newSAXParser()
}
}
导入在中定义的包,就可以开始了。它可以正常工作。经过一些侦查工作后,我尽可能地了解了细节: 试图解析一个开发RESTful接口,我构建了解析器并得到了上面的错误(类似的错误)。我尝试了各种参数来更改XML输出,但得到了相同的错误。我尝试连接到一个XML文档,我很快就创建了一个XML文档(愚蠢地从接口本身抄袭),并得到了相同的错误。然后我尝试连接任何东西,只是为了好玩,然后得到相同的(同样,可能只是相似的)错误 我开始质疑这是源代码错误还是程序错误,所以我开始四处搜索,这看起来像是一个持续的问题——谷歌和其他网站在同一主题上点击了很多次。不幸的是,这使我将注意力集中在错误的上游(语言)方面,而不是在源代码本身的下游进行故障排除 快进一步,解析器突然对原始XML输出起作用。我确认服务器端已经做了一些额外的工作(只是一个疯狂的巧合?)。我没有早期的XML,但怀疑它与正在更改的文档标识符有关 现在,解析器可以在RESTful接口上正常工作,也可以在任何格式良好的XML上正常工作。它在我尝试过的所有XHTML DTD(例如www.w3.org)上都失败了。这与@SeanReilly的预期相反,但似乎与之格格不入 我对Scala还是新手,所以无法确定我是否有特殊或典型的案例。我也不能保证这个问题不会以另一种形式再次发生在我身上。除非使用与@GClaramunt$@J-16 SDiZ使用的解决方案类似的解决方案,否则拉取XHTML将继续导致此错误。我没有资格知道这是否是语言的问题,或者是我的解决方案实现的问题(可能是以后的问题) 就目前的时间范围而言,我认为最好的解决方案是确保能够解析该XML源,而不是看到其他人也有同样的错误,并假设该语言存在功能问题
希望这对其他人有所帮助。对于scala 2.7.7,我使用scala.xml.parsing.XhtmlParser实现了这一点。设置Xerces开关仅在使用Xerces时有效。实体解析器适用于任何JAXP解析器 还有更多的通用实体解析器,但是当我只想解析有效的XHTML时,这个实现就起到了作用 显示缓存DTD和放弃网络流量是多么微不足道
无论如何,这就是我修复它的方法。我总是忘记。我总是犯错误。我总是去拿这个实体解析器。然后我又开始做生意了。格拉拉姆特的解决方案为我创造了奇迹。我的斯卡拉骗局
def fetchAndParseURL(URL:String) = (Http request(URL)) match {
case (true, body) =>
val xml = XML.load(body)
"True"
case _ => "False"
}
def fetchAndParseURL(URL:String) = (Http request(URL)) match {
case (true, body) =>
val f = javax.xml.parsers.SAXParserFactory.newInstance()
f.setNamespaceAware(false)
f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
val MyXML = XML.withSAXParser(f.newSAXParser())
val xml = MyXML.load(body)
"True"
case _ => "False"
}
import scala.xml.Elem
import scala.xml.factory.XMLLoader
import javax.xml.parsers.SAXParser
object MyXML extends XMLLoader[Elem] {
override def parser: SAXParser = {
val f = javax.xml.parsers.SAXParserFactory.newInstance()
f.setNamespaceAware(false)
f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
f.newSAXParser()
}
}
package mypackage
import org.xml.sax.{SAXNotRecognizedException, SAXNotSupportedException}
import com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl
import javax.xml.parsers.ParserConfigurationException
@throws(classOf[SAXNotRecognizedException])
@throws(classOf[SAXNotSupportedException])
@throws(classOf[ParserConfigurationException])
class MyXMLParserFactory extends SAXParserFactoryImpl() {
super.setFeature("http://xml.org/sax/features/validation", false)
super.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false)
super.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false)
super.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false)
}
System.setProperty("javax.xml.parsers.SAXParserFactory", "mypackage.MyXMLParserFactory")