Scala/Java не соблюдает спецификации w3 «избыточного трафика 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)
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, а также Запись в блоге системной группы W3C о том, чтобы не пытаться получить доступ к этому DTD через Интернет.Я также изолировал ошибку в методе XML.load(), который, насколько я могу судить, является методом библиотеки Scala.
Мой вопрос:Как я могу это исправить? Является ли это побочным продуктом моего кода (списанного из Сообщение Рафаэля Феррейры), побочный продукт чего-то специфичного для Java, к которому мне нужно обратиться, как в предыдущая тема, или что-то специфическое для Scala?Где происходит этот вызов и является ли это ошибкой или особенностью?("Это мне?Это она, да?»)
Решение 5
Оно работает.После некоторой детективной работы, подробности, насколько я могу выяснить:
Пытаясь разобрать разрабатываемый RESTful-интерфейс, я собираю парсер и получаю вышеуказанную (скорее аналогичную) ошибку.Я пробую различные параметры, чтобы изменить вывод XML, но получаю ту же ошибку.Пытаюсь подключиться к XML-документу, который быстро накручиваю (списанному тупо из самого интерфейса) и получаю ту же ошибку.Затем я пытаюсь подключиться к чему угодно, просто так, и получаю ту же (опять же, скорее всего, только похожую) ошибку.
Я начал задаваться вопросом, была ли это ошибка в источниках или программе, поэтому я начал искать, и это похоже на постоянную проблему - со многими обращениями Google и SO по одной и той же теме.К сожалению, это заставило меня сосредоточиться на восходящих (языковых) аспектах ошибки, а не на устранении неполадок в самих источниках.
Перенесемся вперед, и парсер внезапно заработает на оригинальный XML-вывод.Я подтвердил, что на стороне сервера была проделана дополнительная работа (просто сумасшедшее совпадение?).У меня нет более раннего XML, но я подозреваю, что это связано с изменением идентификаторов документов.
Теперь парсер отлично работает с интерфейсом RESTful, а также с любым хорошо отформатированным XML, который я могу ему передать.Это также не работает на всех XHTML DTD, которые я пробовал (например.www.w3.org).Это противоречит ожиданиям @SeanReilly, но, похоже, согласуется с что утверждает W3.
Я все еще новичок в Scala, поэтому не могу определить, особый ли у меня случай или типичный.Я также не могу быть уверен, что эта проблема не возникнет у меня снова в другой форме в будущем.Кажется, что вытягивание XHTML будет продолжать вызывать эту ошибку, если вы не используете решение, подобное тем, которые были предложены @GClaramunt $ @J-16 SDiZ.Я не достаточно квалифицирован, чтобы знать, является ли это проблемой языка или моей реализацией решения (вероятно, позднее)
Я подозреваю, что в ближайшем будущем лучшим решением для меня было бы убедиться, что это было возможный проанализировать этот источник XML - вместо того, чтобы видеть, что у других была такая же ошибка, и предполагать, что с языком возникла функциональная проблема.
Надеюсь, это поможет другим.
Другие советы
Я столкнулся с ОДНОЙ проблемой и не нашел элегантного решения (я подумываю опубликовать вопрос в списке рассылки Scala). Тем временем я нашел обходной путь:реализуйте свой собственный SAXParserFactoryImpl, чтобы вы могли установить f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", истинный);свойство.Хорошо, что это не требует каких-либо изменений кода в базе кода 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, чтобы сохранить структуру примера как можно более неизменной.Для реального использования я бы выделил его в объект верхнего уровня и превратил «парсер» в 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()
}
}
Импортируйте пакет, в котором он определен, и все готово.
Решение 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")
Это проблема скалы.В встроенной 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 }
Есть две проблемы с тем, что вы пытаетесь сделать:
- Анализатор xml Scala пытается физически получить DTD, хотя это не следует делать.У J-16 SDiZ, кажется, есть несколько советов по этой проблеме.
- Страница переполнения стека, которую вы пытаетесь проанализировать, не является XML.Это строгий HTML4.
Вторую проблему на самом деле невозможно решить в вашем Scala-коде.Даже если вы решите проблему dtd, вы обнаружите, что исходный код не является допустимым XML (например, пустые теги не закрываются должным образом).
Вам придется либо проанализировать страницу с помощью чего-то помимо синтаксического анализатора XML, либо исследовать, используя такую утилиту, как tidy, для преобразования html в xml.
Мои знания Scala довольно скудны, но не могли бы вы использовать Построение парсера вместо?
val xml = new java.io.File("xmlWithDtd.xml")
val parser = scala.xml.parsing.ConstructingParser.fromFile(xml, true)
val doc = parser.document()
println(doc.docElem)
Для scala 2.7.7 мне удалось сделать это с помощью scala.xml.parsing.XhtmlParser.
Настройка переключателей Xerces работает только в том случае, если вы используете Xerces.Распознаватель сущностей работает для любого парсера JAXP.
Существуют более обобщенные преобразователи сущностей, но эта реализация помогает, когда все, что я пытаюсь сделать, — это проанализировать действительный XHTML.
http://code.google.com/p/java-xhtml-cache-dtds-entityresolver/
Показывает, насколько просто кэшировать DTD и отказаться от сетевого трафика.
В любом случае это как я это исправлю.Я всегда забываю.Я всегда получаю ошибку.Я всегда иду за этим преобразователем сущностей.Тогда я снова в деле.