Чтение одного XML-документа из потока с использованием dom4j - PullRequest
1 голос
/ 22 октября 2008

Я пытаюсь прочитать один XML-документ из потока за раз, используя dom4j, обработать его, а затем перейти к следующему документу в потоке. К сожалению, SAXReader от dom4j (использующий JAXP под обложками) продолжает читать и задыхается от следующего элемента документа.

Есть ли способ заставить SAXReader прекратить чтение потока, как только он найдет конец элемента документа? Есть ли лучший способ сделать это?

Ответы [ 6 ]

1 голос
/ 28 октября 2008

Я смог заставить его работать с какой-то гимнастикой, используя некоторые внутренние классы JAXP:

  • Создание пользовательского сканера, подкласса XMLNSDocumentScannerImpl
    • Создайте пользовательский драйвер, реализацию XMLNSDocumentScannerImpl.Driver, внутри пользовательского сканера, который возвращает END_DOCUMENT, когда видит объявление или элемент. Получите ScannedEntity от fElementScanner.getCurrentEntity (). Если у сущности есть PushbackReader, вытолкните оставшиеся непрочитанные символы в буфере сущностей на считыватель.
    • В конструкторе заменяет fTrailingMiscDriver экземпляром этого пользовательского драйвера.
  • Создание пользовательского класса конфигурации, подкласса XIncludeAwareParserConfiguration, который заменяет базовый DOCUMENT_SCANNER экземпляром этого пользовательского сканера в его конструкторе.
  • Установите экземпляр этого пользовательского класса конфигурации в качестве свойства "com.sun.org.apache.xerces.internal.xni.parser.XMLParserConfiguration", чтобы он создавался при попытке класса SAXReader dom4j создать JAXP XMLReader.
  • При передаче Reader методу SAXReader.read () в dom4j, предоставьте PushbackReader с размером буфера, значительно превышающим односимвольный по умолчанию. По крайней мере 8192 должно быть достаточно для поддержки размера буфера по умолчанию XMLEntityManager внутри JAXP-копии Apache2.

Это не самое чистое решение, поскольку оно включает в себя создание подклассов внутренних классов JAXP, но оно действительно работает.

0 голосов
/ 05 ноября 2008

Я бы прочитал входной поток во внутренний буфер. В зависимости от ожидаемого общего размера потока я либо прочитал бы весь поток и затем проанализировал бы его, либо обнаружил границу между одним xml и следующим (ищите

Единственная реальная разница между обработкой потока с одним xml и потоком с несколькими xmls - это логика буфера и разделения.

0 голосов
/ 29 октября 2008

Я делал это раньше, оборачивая базовый считыватель другим читателем моего собственного создания, который имел очень простую возможность синтаксического анализа. Предполагая, что вы знаете закрывающий тег для документа, оболочка просто анализирует совпадение, например для "". Когда он обнаруживает, что возвращает EOF. Обертку можно сделать адаптивной, анализируя первый открывающий тег и возвращая EOF для соответствующего закрывающего тега. Я обнаружил, что нет необходимости фактически определять уровень для закрывающего тега, поскольку ни в одном документе я не использовал тег документа внутри себя, поэтому было гарантировано, что первое вхождение закрывающего тега завершит работу документа.

Насколько я помню, одним из приемов было закрытие блока-обёртки (), так как считыватель DOM закрывает источник ввода.

Итак, с учетом ввода Reader ваш код может выглядеть так:

SubdocReader sdr=new SubdocReader(input);
while(!sdr.eof()) {
    sdr.next();
    // read doc here using DOM
    // then process document
    }
input.close();

Метод eof () возвращает true, если встречается EOF. Метод next () помечает читателя как прекращение возврата -1 для read ().

Надеюсь, это укажет вам полезное направление.

- Киви.

0 голосов
/ 28 октября 2008

Предполагая, что вы несете ответственность за размещение документов в потоке, должно быть легко разделить документы каким-либо образом. Например:

// Any value that is invalid for an XML character will do.
static final char DOC_TERMINATOR=4;

BOOL addDocumentToStream(BufferedWriter streamOut, char xmlData[])
{
  streamOut.write(xmlData);
  streamOut.write(DOC_TERMINATOR);
}

Затем при чтении из потока считывается в массив, пока не встретится DOC_TERMINATOR.

char *getNextDocuument(BufferedReader streamIn)
{
  StringBuffer buffer = new StringBuffer();
  int character;

  while (true)
  {
    character = streamIn.read();
    if (character == DOC_TERMINATOR)
      break;

    buffer.append(character);
  }
  return buffer.toString().toCharArray();
}

Поскольку 4 - недопустимое значение символа, вы не встретите его, за исключением случаев, когда вы явно добавляете его. Таким образом, вы можете разделить документы. Теперь просто оберните соответствующий массив символов для ввода в SAX и все готово.

...
  XMLReader xmlReader = XMLReaderFactory.createXMLReader();
...
  while (true)
  {
    char xmlDoc = getNextDocument(streamIn);

    if (xmlDoc.length == 0)
      break;

    InputSource saxInputSource = new InputSource(new CharArrayReader(xmlDoc));
    xmlReader.parse(saxInputSource);
  }
...

Обратите внимание, что цикл завершается, когда он получает документ длиной 0. Это означает, что вы должны либо добавить второй DOC_TERMINATOR после того, как последнему документу вам нужно добавить что-то, чтобы обнаружить конец потока в getNextDocument ().

0 голосов
/ 24 октября 2008

Я думаю, вам нужно было бы добавить адаптер, что-то, чтобы обернуть поток, и чтобы эта вещь возвращала конец файла, когда он видит начало следующего документа. Насколько я знаю, синтаксические анализаторы, как написано, будут работать до конца файла или до ошибки ... и обнаружение другого <?xml version="1.0"?> наверняка будет ошибкой.

0 голосов
/ 23 октября 2008

Скорее всего, вы не хотите иметь более одного документа в одном потоке одновременно. Я не думаю, что SAXReader достаточно умен, чтобы остановиться, когда он доберется до конца первого документа. Почему необходимо иметь несколько документов в одном потоке, как это?

...