Scala/Java 是否不遵守 w3 的“过量 dtd 流量”规范?

2022-09-03 05:34:16

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

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


答案 1

我遇到了同样的问题,我还没有找到一个优雅的解决方案(我正在考虑将问题发布到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


答案 2

如果不解决该问题,那么如果函数请求在下面返回 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()
  }
}

导入在其中定义的包,您就可以开始了。


推荐