Scala/Java 是不是不尊重 w3“过量 dtd 流量”规范?

Posted

技术标签:

【中文标题】Scala/Java 是不是不尊重 w3“过量 dtd 流量”规范?【英文标题】:Is Scala/Java not respecting w3 "excess dtd traffic" specs?Scala/Java 是否不尊重 w3“过量 dtd 流量”规范? 【发布时间】:2010-11-08 22:31:01 【问题描述】:

我是 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://***.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 thread,以及关于不尝试通过 Web 访问此 DTD 的 W3C's System Team Blog entry。我还将错误隔离到 XML.load() 方法,据我所知,这是一个 Scala 库方法。

我的问题:我该如何解决这个问题?这是我的代码的副产品(抄自 Raphael Ferreira's post),是我需要解决的 Java 特定问题的副产品吗?就像the previous thread 一样,还是Scala 特有的?这个调用发生在哪里,是错误还是功能? (“是我吗?是她,对吧?”

【问题讨论】:

您已经得到了答案,但我想评论一下 W3C 博客条目:根据 XML 规范,如果您使用 SYSTEM 标识符,那么解析器必须能够检索该内容: xml.com/axml/target.html#dt-sysid——即使它多年来没有改变。我链接到带注释的规范(Tim Bray 的创作,原始规范编辑之一),因为它对 SYSTEM 与 PUBLIC 标识符有一些很好的评论。 @kdgregory,但这不算,因为内容mettadore正在尝试检索不是XML 仔细阅读:“网址无所谓,这是个笑话例子”;找到任何产生有效 XHTML 的网站,你都会遇到同样的问题 @kdgregory url 显然很重要,因为如果您查看错误消息,它正在尝试检索 html4 dtd。所以真正的页面也不是 XML。这种差异在实践中也很重要:考虑到(正如其他人指出的那样)xml 规范(而不是 html 规范)需要它,很可能会成功检索到 xhtml dtd。 :shrug: 当你尝试打开一个指向 "w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" 的 URLConnection 时,它会做同样的事情;显然他们正在查看用户代理并阻止 Java API 【参考方案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 而无需验证

【讨论】:

你的方法简单多了,我喜欢这个。 不错!将以下功能也设置为“false”:apache.org/xml/features/disallow-doctype-declapache.org/xml/features/nonvalidating/load-dtd-grammarapache.org/xml/features/nonvalidating/load-external-dtd 为了更好的品味(并且对名称重构有用):System.setProperty("javax.xml.parsers.SAXParserFactory", classOf[MyXMLParserFactory].getName)【参考方案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()
  

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

【讨论】:

男孩,我在发布代码时真的需要小心。我只是匆匆忙忙地加入了一些东西,草率地删除了无关紧要的东西并忽略了大多数问题。反馈总是有帮助的,但我应该更好地确保我的示例代码鸭子是连续的,这样人们就不会花时间尝试修复无关紧要的辅助问题。很抱歉@Daniel 和其他所有人。 只是重新阅读-并不意味着听起来很尖刻。并不是说它不受欢迎——我实际上使用了你的代码——只是我应该花更多时间清理,而不是让别人花时间。 问题是,我见过人们这样做,并希望它以前在稍微不同的环境中工作。所以我认为最好解决这个问题,因为它可能会很高兴地工作,直到......它没有。 :) 我在编译此代码时遇到问题,XML 对象上似乎不存在 withSAXParser 方法...? 看来XMLLoader 和方法withSAXParser 都只是Scala 2.8。我什至从未考虑过它们在 2.7 上可能不存在的可能性,但它确实存在。【参考方案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")

【讨论】:

【参考方案4】:

这是一个 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     

【讨论】:

使用 DOM 解析器,如果 DOCTYPE 存在,这将导致错误(并且您必须将其设置为属性,而不是功能)。 Scala 解析器包装器真的如此无聊以至于它忽略了命名空间吗?!?我们是在 1997 年吗? @kdgregory: Scala 包装器损坏 --> 同意。忽略命名空间 --> 我猜你从来没有尝试在启用命名空间的情况下解析 RSS/Atom :)【参考方案5】:

它有效。经过一些侦探工作,我能弄清楚的细节:

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

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

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

现在,解析器在 RESTful 接口上运行良好,任何格式良好的 XML 都可以使用。它在我尝试过的所有 XHTML DTD 上也失败了(例如 www.w3.org)。这与@SeanReilly 的预期相反,但似乎与what the W3 states 不谋而合。

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

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

希望这对其他人有所帮助。

【讨论】:

在@Daniel 发布时写了这篇文章,所以错过了。使用他对每个人都否定 doctype 验证的内联实现,解析器可以处理所有我可以扔给它的 XML 和 XHTML。谢谢大家! 我建议在***对象中定义“MyXML”,然后导入它。无需每次都创建新的解析器。 如果你真的想帮助别人,请报告错误。堆栈溢出的所有这些能量,但没有人费心去打开一张可能有人看到的票。 lampsvn.epfl.ch/trac/scala 对此的回应有点晚了,但@extempore,我没有提交错误,因为我不能确定这只是我自己的愚蠢,似乎是这样。由于 XML 解析器正在解析有效的 XML,但会破坏损坏的 XML 和 XHTML,因此它似乎按预期运行,并且错误是我。【参考方案6】:

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

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

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

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

【讨论】:

谢谢,这是一个很好的观点,我明白这一点(网址是个玩笑)。实际上,我使用的 URL 是否有效似乎并不重要。这个问题是针对 DTD 问题的,我试图将代码最小化到足以重现错误的数量。【参考方案7】:

我对 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)

【讨论】:

【参考方案8】:

对于 scala 2.7.7,我设法用 scala.xml.parsing.XhtmlParser 做到了这一点

【讨论】:

在某些情况下,这可能是比 XML 解析器更好的解决方案,因为它允许更宽松的定义。尽管如此,我确实发现 XML 解析器确实适用于所有需要解析的有效 XML 的情况。它似乎只在 XML 损坏时才会中断。我猜解析器的选择可能应该基于用例(当然)。【参考方案9】:

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

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

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

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

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

【讨论】:

以上是关于Scala/Java 是不是不尊重 w3“过量 dtd 流量”规范?的主要内容,如果未能解决你的问题,请参考以下文章

NSFetchedResultsController sectionNameKeyPath不尊重fetchBatchSize

XLPagerTabStrip:不尊重状态栏

npm update 不尊重 npm outdated 的结果

文本溢出:省略号不被尊重

GCC 不尊重“pragma GCC diagnostic”以消除警告 [重复]

Matlab 的 crossval 方法是不是尊重类频率?