Мне нужно отправить большой объем данных через XML, и моему контейнеру Docker не хватает памяти при выполнении задачи. Есть ли способ с помощью Go постепенно маршалировать большой XML-документ, а также постепенно записывать его в файл, чтобы минимизировать использование памяти?
Прямо сейчас я собираю все данные, которые должны быть в документе (4+ ГБ), упорядочиваю их с помощью xml.Marshal
, а затем использую ioutil.WriteFile
.
Используйте xml.Encoder
для потоковой передачи вывода XML в io.Writer
, который может быть сетевым подключением (net.Conn
) или файлом (os.File
). Полный результат не будет сохранен в памяти.
Вы можете использовать Encoder.Encode()
для кодирования значения Go в XML. Как правило, вы можете передать любое значение Go, которое вы бы передали в xml.Marshal()
.
Encoder.Encode()
помогает только в том случае, если данные, которые вы хотите маршалировать, готовы в памяти, что может быть или не быть для вас возможным. Например. если вы хотите маршалировать большой список, который не может (или не должен) быть прочитан в память, это не будет для вас спасением.
Если входные данные также не могут храниться в памяти, вы можете построить вывод XML с помощью токенов и элементов. Вы можете использовать для этого Encoder.EncodeToken()
, что позволяет вам писать «части» результирующего XML-документа.
Например, если вы хотите записать в вывод большой список, вы можете написать тег начального элемента (например, <list>
), а затем записать элементы списка один за другим (каждый из которых извлекается из базы данных или из файла, или создается с помощью алгоритм «на лету»), и после маршалинга списка вы можете закрыть тег элемента списка (</list>
).
Вот простой пример того, как вы можете это сделать:
type Student struct {
ID int
Name string
}
func main() {
he := func(err error) {
if err != nil {
panic(err) // In your app, handle error properly
}
}
// For demo purposes we use an in-memory buffer,
// but this may be an os.File too.
buf := &bytes.Buffer{}
enc := xml.NewEncoder(buf)
enc.Indent("", " ")
he(enc.EncodeToken(xml.StartElement{Name: xml.Name{Local: "list"}}))
for i := 0; i < 3; i++ {
// Here you can fetch / construct the records
he(enc.Encode(Student{ID: i, Name: string(i + 'A')}))
}
he(enc.EncodeToken(xml.EndElement{Name: xml.Name{Local: "list"}}))
he(enc.Flush())
fmt.Println(buf.String())
}
Вывод вышеизложенного (попробуйте на Игровая площадка):
<list>
<Student>
<ID>0</ID>
<Name>A</Name>
</Student>
<Student>
<ID>1</ID>
<Name>B</Name>
</Student>
<Student>
<ID>2</ID>
<Name>C</Name>
</Student>
</list>
Как вы это делаете в настоящее время? Вам не подходит
xml.Encoder()
?