2009-07-08 19 views
15

Jestem nowy w Scala, więc mogę być poza tym, chcę wiedzieć, czy problem jest mój kod. Biorąc pod uwagę plików httpparse Scala, uproszczone do:Czy Scala/Java nie przestrzega specyfikacji "wlotu dtd" w w3?

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" 
    } 
} 

który jest prowadzony z (URL nie ma znaczenia, jest to przykład żart):

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

Wynik niezmiennie:

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... 

Zobaczyłem Stack Overflow thread na ten temat w odniesieniu do Java, a także W3C's System Team Blog entry o nie próbuje uzyskać dostęp do DTD za pośrednictwem Internetu. Wyodrębniłem również błąd metody XML.load(), która jest metodą biblioteki Scala, o ile wiem.

Moje pytanie: Jak mogę to naprawić? Czy jest to coś, co jest produktem mojego kodu (cribbed z Raphael Ferreira's post), jest produktem czegoś specyficznego dla Javy, który muszę adresować jak w the previous thread, lub czymś, co jest specyficzne dla Scala? Gdzie to się dzieje, i czy jest to błąd lub funkcja? („Czy to ja To ona, prawda?”)

+1

Masz już odpowiedź, ale ja chcesz skomentować wpis blogu W3C: zgodnie ze specyfikacją XML, jeśli używasz identyfikatora SYSTEM, parser musi mieć możliwość pobrania tej treści: http://www.xml.com/axml/target.html#dt- sysid - nawet jeśli nie zmieniło się od lat. Połączyłem się ze specyfikacją z adnotacjami (stworzenie Tima Braya, jednego z oryginalnych edytorów specyfikacji), ponieważ ma ładny komentarz do identyfikatorów SYSTEM i PUBLIC. – kdgregory

+0

@kdgregory, ale to się nie liczy, ponieważ mettadore zawartości próbuje odzyskać * nie jest XML *. –

+1

przeczytaj uważniej: "URL nie ma znaczenia, to jest przykład na żart"; znajdź jakąkolwiek stronę, która produkuje poprawny XHTML i będziesz mieć ten sam problem – kdgregory

Odpowiedz

2

Działa. Po pracy detektywa, najlepiej jak mogę je wymyślić:

Próbuję parsować rozwojowy interfejs RESTful, buduję parser i otrzymuję powyższy (raczej podobny) błąd. Próbuję różnych parametrów, aby zmienić wyjście XML, ale otrzymuję ten sam błąd. Próbuję połączyć się z dokumentem XML, który szybko pobudzam (głupio wytyka się z samego interfejsu) i otrzymuję ten sam błąd. Potem próbuję połączyć się z niczym, tylko dla kopnięć, i uzyskać ten sam (ponownie, prawdopodobnie tylko podobny) błąd.

Zacząłem pytać, czy to był błąd związany ze źródłami lub programem, więc zacząłem szukać i wygląda to na ciągły problem - z wieloma działaniami Google i SO na ten sam temat. To, niestety, sprawiło, że skupiłem się na aspektach błędów (w języku angielskim), zamiast rozwiązywać więcej problemów w samych źródłach.

Szybkie przewijanie do przodu i analizator składni nagle działa na wyjściowym pliku oryginalnego. Potwierdziłem, że wykonano dodatkową pracę po stronie serwera (tylko zwariowany zbieg okoliczności?). Nie mam wcześniejszego pliku XML, ale podejrzewam, że jest on związany ze zmianą identyfikatorów dokumentów.

Teraz parser działa poprawnie w interfejsie RESTful, a także dobrze sformatowanym XML, który mogę na nim rzucić. Nie działa również we wszystkich testach DTD XHTML, które wypróbowałem (np. Www.w3.org). Jest to sprzeczne z oczekiwaniami @SeanReilly, ale wydaje się jive z what the W3 states.

Wciąż jestem nowy w Scali, więc nie mogę ustalić, czy mam specjalny, czy typowy przypadek. Nie mogę też być pewny, że ten problem nie powtórzy się dla mnie w innej formie. Wygląda na to, że ciągnięcie XHTML nadal będzie powodowało ten błąd, chyba że użyje się rozwiązania podobnego do zaproponowanego przez @GClaramunt $ @ J-16 SDiZ. Nie mam pojęcia, czy jest to problem z językiem, czy z implementacją rozwiązania (prawdopodobnie później). W najbliższym czasie podejrzewam, że najlepszym rozwiązaniem byłoby dla mnie, aby upewnij się, że możliwe jest przeanalizowanie tego źródła XML, a nie zobaczenie, że inne miały ten sam błąd i zakładają, że wystąpił problem funkcjonalny z tym językiem.

Mam nadzieję, że to pomoże innym.

+0

Napisałem to jako @Daniel opublikowany, więc go przegapiłem. Korzystając z wbudowanej implementacji negacji wszystkich osób sprawdzania doktryny, parser działa na wszystkich plikach XML i XHTML, które mogę na niego rzucić. Dziękuje wszystkim! – JohnMetta

+0

Zalecam zdefiniowanie tego "MyXML" wewnątrz obiektu najwyższego poziomu i importowanie go. Nie trzeba za każdym razem tworzyć nowego analizatora składni. –

+0

Jeśli naprawdę chcesz pomóc innym, zgłoś błąd. Cała ta energia na przepełnienie stosu, ale nikt nie był na tyle przejęty, by otworzyć bilet, w którym ktoś mógłby go zobaczyć. http://lampsvn.epfl.ch/trac/scala/ – extempore

1

Istnieją dwa problemy z tym, co staramy się robić:

  • Scala XML parser stara się fizycznie odzyskać DTD kiedy powinnam "t. J-16 SDiZ wydaje się mieć kilka rad dla tego problemu.
  • Strona przepełnienia stosu, którą próbujesz analizować, nie jest XML. Jest ścisły Html4.

Drugi problem nie jest możliwy do naprawienia w kodzie SCALA. Nawet gdy rozwiążesz problem dtd, przekonasz się, że źródło nie jest poprawne XML (puste tagi nie są na przykład poprawnie zamknięte).

Musisz albo sparsować stronę z czymś poza parserem XML, albo zbadać przy użyciu narzędzia takiego jak schludny, aby przekonwertować html na xml.

+0

Dzięki, To dobry punkt, i rozumiem to (adres URL był żartem). Właściwie nie ma znaczenia, jakiego adresu URL używam, czy jest to poprawny XML, czy nie. To pytanie jest specyficzne dla problemu DTD i starałem się zminimalizować kod do tej ilości tylko tyle, aby odtworzyć błąd. – JohnMetta

2

To jest problem scala. Natywna Java ma opcję wyłączenia ładowania DTD:

f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); 

Nie ma odpowiednika w scala.

Jeśli nieco chcesz go naprawić samemu sprawdzić scala/xml/parsing/FactoryAdapter.scala i umieścić linię

278 def loadXML(source: InputSource): Node = { 
279  // create parser 
280  val parser: SAXParser = try { 
281  val f = SAXParserFactory.newInstance() 
282  f.setNamespaceAware(false) 

< - wstaw tutaj

283  f.newSAXParser() 
284  } catch { 
285  case e: Exception => 
286   Console.err.println("error: Unable to instantiate parser") 
287   throw e 
288  } 
+0

W przypadku parsera DOM spowoduje to błąd, jeśli obecny jest DOCTYPE (i trzeba ustawić go jako atrybut, a nie funkcję). A czy wrapper parsera Scala jest tak zszokowany, że ignoruje przestrzenie nazw?!? Czy jesteśmy w 1997 roku? – kdgregory

+0

@kdgregory: Opakowanie Scala jest zepsute -> zgadzam się. Ignoruj ​​przestrzeń nazw -> Wydaje mi się, że nigdy nie próbowałaś parsować kanałów RSS/Atom w środowisku naturalnym z włączoną przestrzenią nazw :) –

0

Moja znajomość Scala jest dość słaba, ale mogłem” t zamiast tego używasz 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) 
11

ja wpadłem na ten sam problem, i nie znalazłem eleganckie rozwiązanie (myślę, że do zaksięgowania pytanie do listy Scala) Tymczasem znalazłem rozwiązania: zaimplementować własną SAXParserFactoryImpl, dzięki czemu możesz ustawić f.setFeature ("http://apache.org/xml/features/disallow-doctype-decl", true); własność.Dobrą rzeczą jest to, że nie wymaga ona zmiany kodu w bazie kodu Scala (zgadzam się jednak, że należy to naprawić). pierwsze mam rozszerzenie domyãlnego parsera:

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); 
     } 
    } 

Nic specjalnego, po prostu chcę szansę ustawić właściwość.

(Uwaga: to jest to zwykły kod Java, prawdopodobnie można napisać to samo w Scala zbyt)

I w kodzie Scala, trzeba skonfigurować JVM do korzystania z nowej fabryki:

System.setProperty("javax.xml.parsers.SAXParserFactory", "mypackage.MyXMLParserFactory"); 

Następnie można wywołać XML.load bez walidacji

+0

Twoja metoda jest znacznie prostsza, uwielbiam to. –

+0

fajnie! Ustaw następujące opcje także na "fałsz": http://apache.org/xml/features/disallow-doctype-decl http://apache.org/xml/features/nonvalidating/load-dtd-grammar http://apache.org/xml/features/nonvalidating/load-external-dtd –

+1

Dla lepszego smaku (i przydatnego do refaktoryzacji nazw): 'System.setProperty (" javax.xml.parsers.SAXParserFactory ", classOf [MyXMLParserFactory ] .getName) ' – juanmirocks

7

bez adresowania, na razie problemu, czego można spodziewać się zdarzyć, jeżeli żądanie funkcja return false poniżej?

def fetchAndParseURL(URL:String) = {  
    val (true, body) = Http request(URL) 

Co będzie zdarzyć, że zostanie wyrzucony wyjątek. Można go przerobić w ten sposób, iż:

def fetchAndParseURL(URL:String) = (Http request(URL)) match {  
    case (true, body) =>  
    val xml = XML.load(body) 
    "True" 
    case _ => "False" 
} 

Teraz, aby rozwiązać problem parsowania XML, DTD będziemy wyłączyć ładowanie przez parser, jak sugerowano przez innych:

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" 
} 

Teraz umieścić rzeczy MyXML wewnątrz fetchAndParseURL tylko po to, aby zachować strukturę przykładu w niezmienionej formie. Do faktycznego wykorzystania, chciałbym rozdzielić je w obiekcie najwyższego poziomu, i uczynić „parser” w def zamiast val, aby uniknąć problemów z modyfikowalnych parserami:

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() 
    } 
} 

importu pakiet jest zdefiniowana w, i jesteś dobry, aby iść.

+0

Chłopcze, naprawdę muszę być ostrożny przy wysyłaniu kodu. Po prostu coś wtrąciłem, niechlujnie usuwając rzeczy, które nie mają znaczenia i ignorując większość problemów. Informacja zwrotna jest zawsze pomocna, ale powinienem być o wiele lepszy, upewniając się, że moje przykładowe kaczki są ułożone w rzędzie, aby ludzie nie spędzali czasu próbując naprawić pomocnicze rzeczy, kiedy nie mają znaczenia. Przepraszam za ten @Daniel i wszystkich innych. – JohnMetta

+0

Po prostu przeczytaj to, co nie znaczy, że brzmi nieciekawie. Nie mówię, że jest niedoceniona - użyłem twojego kodu - po prostu powinienem spędzać więcej czasu na sprzątaniu, niż wydawać je innym. – JohnMetta

+0

Chodzi o to, że widziałem, jak ludzie to robią i oczekują, że zadziała w nieco innym otoczeniu. Pomyślałem więc, że lepiej to zająć, ponieważ może to sprawnie działać do ... nie. :) –

0

do Scala 2.7.7 udało mi się to zrobić z scala.xml.parsing.XhtmlParser

+0

To prawdopodobnie lepsze rozwiązanie niż parser XML dla niektórych przypadków, ponieważ pozwoliłoby to na znacznie luźniejsze definicje. Mimo to okazało się, że analizator składni XML działa w każdym przypadku, gdy istnieje poprawny plik XML do przeanalizowania. Wydaje się, że łamie się tylko wtedy, gdy jest uszkodzony XML. Domyślam się, że wybór parsera powinien być oparty na przypadku użycia (oczywiście). – JohnMetta

0

Ustawienie przełączników Xerces działa tylko jeśli używasz Xerces. Rozdzielacz encji działa dla dowolnego parsera JAXP.

Istnieje więcej ogólnych rozstrzygnięć encji, ale ta implementacja działa, gdy wszystko, co próbuję zrobić, to analizować poprawny XHTML.

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

Pokazuje jak banalne jest, aby buforować DTD i zrezygnować z ruchu sieciowego.

W każdym razie jest to , jak to naprawić. Zawsze zapominam. Zawsze dostaję błąd. Zawsze idę pobrać ten resolwer encji. Potem wracam do pracy.

3

Rozwiązanie GClaramunt działało dla mnie cuda. Moja przemiana Scala jest następujący:

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) 
} 

Jak wspomniano swój oryginalny post, należy zamieścić następującą linię w kodzie gdzieś:

System.setProperty("javax.xml.parsers.SAXParserFactory", "mypackage.MyXMLParserFactory")