Пакет encoding/xml
обеспечивает тип среднего уровня xml.Decoder
.Это позволяет вам читать входной поток XML по одному Token
за раз, мало чем отличаясь от старой потоковой модели Java SAX.Когда вы найдете то, что ищете, вы можете вернуться к decoder.Decode
, чтобы запустить обычную последовательность демаршалинга, чтобы вывести отдельные объекты.Просто помните, что поток токенов может содержать несколько «нерелевантных» вещей (текстовые узлы только для пробелов, инструкции по обработке, комментарии), и вам нужно пропустить их, по-прежнему ища «важные» вещи (текст без пробелов)узлы, неожиданные элементы начала / конца).
В качестве примера высокого уровня, если вы ожидаете очень большое сообщение SOAP со списком записей, вы можете выполнять потоковый анализ до тех пор, пока не увидите<soap:Body>
start-element, проверьте, что его непосредственный дочерний элемент ( например, , следующий start-элемент) является ожидаемым элементом, и затем вызовите decoder.Decode
для каждого из его дочерних элементов.Если вы видите конец элемента операции, вы можете развернуть дерево элементов (теперь вы ожидаете увидеть </soap:Body></soap:Envelope>
).Все остальное является ошибкой, которую вам нужно перехватить и обработать.
Скелет приложения здесь может выглядеть так:
type Foo struct {
Name string `xml:"name"`
}
decoder := xml.NewDecoder(r)
for {
t, err := decoder.Token()
if err != nil {
panic(err)
}
switch x := t.(type) {
case xml.StartElement:
switch x.Name {
case xml.Name{Space: "", Local: "foo"}:
var foo Foo
err = decoder.DecodeElement(&foo, &x)
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", foo)
default:
fmt.Printf("Unexpected SE {%s}%s\n", x.Name.Space, x.Name.Local)
}
case xml.EndElement:
switch x.Name {
default:
fmt.Printf("Unexpected EE {%s}%s\n", x.Name.Space, x.Name.Local)
}
}
}
https://play.golang.org/p/_ZfG9oCESLJ имеет полный рабочий пример (не в случае SOAP, а в чем-то меньшем).
Синтаксический анализ XML в Go, как и в принципе все остальное, представляет собой «вытягивающую» модель: вы говорите читателю, что читать, и он получает данные из io.Reader
Вы даете это.Если вы вручную создаете xml.Decoder
, вы можете извлечь из него один токен за один раз, и это, вероятно, вызовет r.Read
в удобоваримых кусках, но вы не можете вставлять крошечные приращения данных в анализатор, как вы предлагаете.
Я не могу конкретно говорить о производительности encoding/xml
, но подобный подход с гибридной потоковой передачей, по крайней мере, увеличит задержку при первом выводе и сохранит меньше оперативных данных в памяти за один раз.