15

我是 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)
         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"
   }
}

哪个运行(URL无关紧要,这是一个笑话示例):

scala> HTTPParse.fetchAndParseURL("http://stackoverflow.com")

结果总是:

   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 的Stack Overflow 线程,以及W3C 的系统团队博客条目关于不尝试通过网络访问此 DTD。我还将错误隔离到 XML.load() 方法,据我所知,这是一个 Scala 库方法。

我的问题:我该如何解决这个问题?这是我的代码的副产品(摘自Raphael Ferreira 的帖子),我需要在上一个线程中解决的 Java 特定的副产品,还是 Scala 特定的副产品?这个调用发生在哪里,是错误还是功能?(“是我吗?是她,对吧?”

4

9 回答 9

11

我遇到了同样的问题,但我还没有找到一个优雅的解决方案(我正在考虑将问题发布到 Scala 邮件列表)同时,我找到了一个解决方法:实现你自己的 SAXParserFactoryImpl 以便你可以设置 f .setFeature(" http://apache.org/xml/features/disallow-doctype-decl ", 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 无需验证

于 2009-07-08T15:39:07.973 回答
7

暂时不解决这个问题,如果下面的函数请求返回 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 中,只是为了尽可能保持示例的结构不变。对于实际使用,我会将它分隔在一个顶级对象中,并将“解析器”转换为 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()
  }
}

导入它定义的包,你就可以开始了。

于 2009-07-08T18:48:23.737 回答
3

GClaramunt 的解决方案为我创造了奇迹。我的 Scala 转换如下:

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")
于 2011-05-01T02:00:48.403 回答
2

这是一个scala问题。Native 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)

<-- 在此插入

283       f.newSAXParser()
284     } catch {
285       case e: Exception =>
286         Console.err.println("error: Unable to instantiate parser")
287         throw e
288     }
于 2009-07-08T05:59:01.347 回答
2

有用。经过一些侦探工作,我能想到的细节是:

试图解析一个开发的 RESTful 接口,我构建了解析器并得到了上述(相当类似的)错误。我尝试了各种参数来更改 XML 输出,但得到了同样的错误。我尝试连接到一个我快速启动的 XML 文档(从界面本身愚蠢地抄袭)并得到同样的错误。然后我尝试连接任何东西,只是为了好玩,并得到相同的(同样,可能只是相似的)错误。

我开始怀疑是源错误还是程序错误,所以我开始四处搜索,这看起来像是一个持续存在的问题——许多谷歌和 SO 都在同一主题上点击。不幸的是,这使我专注于错误的上游(语言)方面,而不是在源头本身对下游进行故障排除。

快进,解析器突然处理原始XML 输出。我确认服务器端已经完成了一些额外的工作(只是一个疯狂的巧合?)。我没有早期的 XML,但怀疑它与正在更改的文档标识符有关。

现在,解析器可以在 RESTful 接口上正常工作,以及我可以扔给它的任何格式良好的 XML。它在我尝试过的所有 XHTML DTD 上也失败了(例如 www.w3.org)。这与@SeanReilly 的预期相反,但似乎与W3 的声明不符。

我还是 Scala 的新手,所以无法确定我是否有特殊情况或典型情况。我也不能保证这个问题不会以另一种形式再次发生在我身上。似乎拉 XHTML 将继续导致此错误,除非使用类似于@GClaramunt $ @J-16 SDiZ 所建议的解决方案。我真的没有资格知道这是否是语言问题,或者我的解决方案实施(可能是后者)

对于即时的时间范围,我怀疑最好的解决方案是让我确保可以解析那个 XML 源——而不是看到其他人有同样的错误并假设语言存在功能问题.

希望这对其他人有帮助。

于 2009-07-08T19:45:20.283 回答
1

您尝试做的事情有两个问题:

  • Scala 的 xml 解析器在不应该的时候尝试物理检索 DTD。J-16 SDiZ 似乎对这个问题有一些建议。
  • 您尝试解析的堆栈溢出页面不是 XML。这是 Html4 严格的。

第二个问题实际上无法在您的 scala 代码中解决。即使您解决了 dtd 问题,您也会发现源代码不是有效的 XML(例如,空标签没有正确关闭)。

您必须使用除 XML 解析器之外的其他东西来解析页面,或者使用 tidy 之类的实用程序进行调查以将 html 转换为 xml。

于 2009-07-08T05:58:14.173 回答
0

我对 Scala 的了解很差,但你不能用ConstructingParser代替吗?

  val xml = new java.io.File("xmlWithDtd.xml")
  val parser = scala.xml.parsing.ConstructingParser.fromFile(xml, true)
  val doc = parser.document()
  println(doc.docElem)
于 2009-07-08T12:39:06.267 回答
0

对于 scala 2.7.7,我设法使用 scala.xml.parsing.XhtmlParser

于 2009-12-15T12:35:33.607 回答
0

仅当您使用 Xerces 时,设置 Xerces 开关才有效。实体解析器适用于任何 JAXP 解析器。

那里有更通用的实体解析器,但是当我试图做的只是解析有效的 XHTML 时,这个实现就可以了。

http://code.google.com/p/java-xhtml-cache-dtds-entityresolver/

显示缓存 DTD 并放弃网络流量是多么的简单。

无论如何,这就是我修复它的方法。我总是忘记。我总是得到错误。我总是去获取这个实体解析器。然后我又回来做生意了。

于 2010-01-02T22:57:39.833 回答