Блоки синтаксического анализатора Java XML (очень необычно и странно!) - PullRequest
1 голос
/ 02 апреля 2011

У меня очень странный случай:

Я попытался проанализировать несколько веб-сайтов, соответствующих XHTML, с использованием анализатора (ов) Java XML по умолчанию. Тестовые блоки при разборе (не при загрузке).

Может ли это быть ошибкой или парсер пытается загрузить дополнительные ссылочные ресурсы во время синтаксического анализа (что было бы "хорошей" антифункцией)?

С простыми данными, это работает. (TEST1)
Со сложными данными это блокирует. (TEST2)
(Я пробовал en.wikipedia.org и validator.w3.org)

Когда происходит блокировка, процессор простаивает.

Протестировано с JDK6 и JDK7, те же результаты.

См. Контрольный пример, источник готов к копированию + вставке + запуск.

Источник

import java.io.*;
import java.net.*;
import java.nio.charset.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import org.w3c.dom.*;

public class _XmlParsingBlocks {

  private static Document parseXml(String data)
      throws Exception {
    Transformer t = TransformerFactory.newInstance().newTransformer();
    DocumentBuilder b = DocumentBuilderFactory.newInstance().newDocumentBuilder();
    DOMResult out = new DOMResult(b.newDocument());
    t.transform(new StreamSource(new StringReader(data)), out);
    return (Document) out.getNode();
  }

  private static byte[] streamToByteArray(InputStream is)
      throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();

    for (;;) {
      byte[] buffer = new byte[256];
      int count = is.read(buffer);
      if (count == -1) {
        is.close();
        break;
      }
      baos.write(buffer, 0, count);
    }

    return baos.toByteArray();
  }

  private static void test(byte[] data)
      throws Exception {
    String asString = new String(data, Charset.forName("UTF-8"));

    System.out.println("===== PARSING STARTED =====");
    Document doc = parseXml(asString);
    System.out.println("===== PARSING ENDED =====");
  }

  public static void main(String[] args)
      throws Exception {
    {
      System.out.println("********** TEST 1");
      test("<html>test</html>".getBytes("UTF-8"));
    }

    {
      System.out.println("********** TEST 2");
      URL url = new URL("http://validator.w3.org/");
      URLConnection connection = url.openConnection();
      InputStream is = connection.getInputStream();
      byte[] data = streamToByteArray(is);
      System.out.println("===== DOWNLOAD FINISHED =====");

      test(data);
    }
  }

}

выход

********** TEST 1
===== PARSING STARTED =====
===== PARSING ENDED =====
********** TEST 2
===== DOWNLOAD FINISHED =====
===== PARSING STARTED =====

[here it blocks]

Ответы [ 4 ]

2 голосов
/ 03 апреля 2011

W3C в последние несколько месяцев начал блокировать запросы на общие DTD, такие как XHTML DTD - они не могут справиться с генерируемым трафиком.Если вы не используете прокси-сервер, который кэширует DTD, вам потребуется использовать EntityResolver или каталог для перенаправления ссылок на локальную копию.

2 голосов
/ 02 апреля 2011

Глядя на страницу, которую вы скачали, она содержит еще несколько http: URL.

Это начало:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

Я могу представить, что анализатор XML пытается загрузитьссылка DTD здесь, чтобы иметь возможность проверить содержимое XML.

Попробуйте добавить преамбулу к вашему простому документу, или попробуйте удалить ее от вашего сложного документа, чтобы увидеть, если это что-то меняет.

Переключите анализатор на неподтвержденный и посмотрите, поможет ли это.(В качестве альтернативы, есть несколько опций для настройки поведения синтаксического анализатора - например, setURIResolver выглядит хорошо.)

1 голос
/ 02 апреля 2011

Решение: предварительная выборка (или лучше: использование автономных сохраненных) DTD для пользовательского EntityResolver.

Когда ожидается, что никакие внешние объекты XML не используются (например, &nbsp;), пустойInputSource можно вернуть, см. Внутреннее перечисление.В противном случае подготовленное отображение DTD URI -> bytearray может использоваться для предотвращения загрузки DTD в режиме онлайн.

Класс

import java.io.*;
import java.util.*;
import javax.annotation.*;
import org.xml.sax.*;

public final class PrefetchedEntityResolver
    implements EntityResolver {

  /**
  * NOTE: {@see #RETURN_NULL} seems to cause default behavior
  * (which is: downloading the DTD);
  * use {@see #RETURN_EMPTY_DATA} to ensure "offline" behavior
  * (which could lead to entity parsing errors).
  */
  public static enum NoMatchBehavior {

    THROW_EXCEPTION, RETURN_NULL, RETURN_EMPTY_DATA;
  }

  private final SortedMap<String, byte[]> prefetched;
  private final NoMatchBehavior noMatchBehavior;

  public PrefetchedEntityResolver(NoMatchBehavior noMatchBehavior,
      @Nullable SortedMap<String, byte[]> prefetched) {
    this.noMatchBehavior = noMatchBehavior;
    this.prefetched = new TreeMap<>(prefetched == null
        ? Collections.<String, byte[]>emptyMap() : prefetched);
  }

  @Override
  public InputSource resolveEntity(String name, String uri)
      throws SAXException, IOException {
    byte[] data = prefetched.get(uri);
    if (data == null) {
      switch (noMatchBehavior) {
      case RETURN_NULL:
        return null;
      case RETURN_EMPTY_DATA:
        return new InputSource(new ByteArrayInputStream(new byte[]{}));
      case THROW_EXCEPTION:
        throw new SAXException("no prefetched DTD found for: " + uri);
      default:
        throw new Error("unsupported: " + noMatchBehavior.toString());
      }
    }

    return new InputSource(new ByteArrayInputStream(data));
  }

}

Использование

public static Document parseXml(byte[] data)
    throws Exception {
  DocumentBuilderFactory df = DocumentBuilderFactory.newInstance();
  df.setValidating(false);
  df.setXIncludeAware(false);
  df.setCoalescing(false);
  df.setExpandEntityReferences(false);

  DocumentBuilder b = df.newDocumentBuilder();
  b.setEntityResolver(new PrefetchedEntityResolver(
      PrefetchedEntityResolver.NoMatchBehavior.RETURN_EMPTY_DATA,
      /* pass some prepared SortedMap<String, byte[]> */));
  ByteArrayInputStream bais = new ByteArrayInputStream(data);
  return b.parse(bais);
}
0 голосов
/ 02 апреля 2011

Возможно, ваше условие "count == -1" должно стать "count <= 0"? </p>

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...