Как читать xml контент с разными корнями с golang encoding / xml - PullRequest
0 голосов
/ 20 февраля 2020

Моя текущая задача - прочитать различные полезные данные xml, поступающие в систему с golang. Полезные данные xml могут иметь разные имена тегов root. Другое имя тега root означает, что xml имеет совершенно другое значение. Я уже давно играю с кодировкой / xml, но я не нашел способа прочитать эти xml полезные нагрузки одним вызовом Unmarshal, не манипулируя им перед или не выполняя предварительный анализ, чтобы извлеките имя root.

Это упрощенный пример выполняемой задачи, отдельно читая 2 xmls:

package main

import (
    "fmt"
    "encoding/xml"
)

type Boxes struct {
    Length []float64
}

type Bottles struct {
    Diameter []float64
}   

var Box_xml = []byte("<Boxes><Length>45</Length><Length>41</Length></Boxes>")
var Bottle_xml = []byte("<Bottles><Diameter>23</Diameter><Diameter>25</Diameter></Bottles>")

func main() {
    box := Boxes{}
    xml.Unmarshal(Box_xml, &box)
    bottle := Bottles{}
    xml.Unmarshal(Bottle_xml, &bottle)
    fmt.Println(box, bottle)
}

Этот код печатает

{[45 41]} {[23 25]}

Мое текущее «лучшее» решение для обработки произвольного входящего сообщения за один вызов - добавить тег root к этим xmls и прочитать их в центральной структуре, например:

package main

import (
    "fmt"
    "encoding/xml"
)

type Boxes struct {
    Length []float64
}

type Bottles struct {
    Diameter []float64
}

type Delivery struct {
    Boxes Boxes
    Bottles Bottles
}   

var Box_xml = []byte("<Delivery><Boxes><Length>45</Length><Length>41</Length></Boxes></Delivery>")
var Bottle_xml = []byte("<Delivery><Bottles><Diameter>23</Diameter><Diameter>25</Diameter></Bottles></Delivery>")

func main() {
    delivery := Delivery{}
    xml.Unmarshal(Box_xml, &delivery)
    fmt.Println(delivery )
    delivery = Delivery{}
    xml.Unmarshal(Bottle_xml, &delivery)
    fmt.Println(delivery)
}

Этот код печатает

{{[45 41]} {[]}}
{{[]} {[23 25]}}

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

Как я могу это сделать без добавления искусственного тега root в полезную нагрузку?

1 Ответ

0 голосов
/ 28 февраля 2020

Вы можете создать xml декодер и использовать метод Token для синтаксического анализа xml входного узла за узлом. Он не будет анализировать весь документ. Подробнее здесь .

Token возвращает следующий токен XML во входном потоке. В конце входного потока Token возвращает nil, io.EOF.

Срезы байтов в возвращенных данных токена относятся к внутреннему буферу синтаксического анализатора и остаются действительными только до следующего вызова Token. Чтобы получить копию байтов, вызовите CopyToken или метод Copy токена.

Token расширяет самозакрывающиеся элементы, такие как в отдельные начальные и конечные элементы, возвращаемые последовательными вызовами.

Токен гарантирует, что возвращаемые токены StartElement и EndElement будут правильно вложены и сопоставлены: если токен встречает неожиданный конечный элемент или EOF перед всеми ожидаемыми конечными элементами, он будет вернуть ошибку.

Это всего лишь пример того, как вы можете определить, какой тип xml у вас есть и в какую структуру вам нужно разобраться:

package main

import (
    "encoding/xml"
    "log"
    "strings"
)

type Boxes struct {
    Length []float64
}

type Bottles struct {
    Diameter []float64
}

type DeliveryBoxes struct {
    Boxes   Boxes
}

type DeliveryBottles struct {
    Bottles Bottles
}

const (
    BoxXML    = "<Delivery><Boxes><Length>45</Length><Length>41</Length></Boxes></Delivery>"
    BottleXML = "<Delivery><Bottles><Diameter>23</Diameter><Diameter>25</Diameter></Bottles></Delivery>"
)

func main() {
    var err error
    var xmlReader = strings.NewReader(BoxXML)
    var decoder = xml.NewDecoder(xmlReader)

    // start with a first token 'Delivery'
    t, err := decoder.Token()
    if err != nil {
        log.Fatalln(err)
    }

    // next token is a next node: 'Boxes' or 'Bottles'
    t, err = decoder.Token()
    if err != nil {
        log.Fatalln(err)
    }

    startElement := t.(xml.StartElement)

    // create new reader to start from the beginning
    xmlReader = strings.NewReader(BoxXML)

    switch startElement.Name.Local {
    case "Boxes":
        var delivery DeliveryBoxes
        if err := xml.NewDecoder(xmlReader).Decode(&delivery); err != nil {
            log.Fatalln(err)
        }
        log.Println(delivery)
    case "Bottles":
        var delivery DeliveryBottles
        if err := xml.NewDecoder(xmlReader).Decode(&delivery); err != nil {
            log.Fatalln(err)
        }
        log.Println(delivery)
    }
}

В В общем, мы анализируем первые два узла для проверки типа структуры, а затем анализируем полный xml ввод с использованием определенной структуры. Вы можете переместить оператор switch в одну функцию и использовать тип interface{} для переменной delivery. В любом случае, это пример, показывающий, что вы можете определить формат данных вашего xml ввода, не анализируя весь ввод.

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