#xml #go
#xml #Вперед
Вопрос:
Я разархивирую xml, но я не могу получить тот же результат при использовании MarshalXML (см. Пример ниже). Как вы можете видеть, xs:simpleType преобразуется в simpleType и другие отличия. Как я могу создать полную идентификацию? Мне нужно преобразовать произвольный xml в соответствии с определенными правилами, поэтому я не могу использовать определенные теги.
Вы можете просмотреть рабочий код здесь:https://play.golang.org/p/QK2AfPsR9ZN
package main
import (
"bytes"
"encoding/xml"
"fmt"
)
var data = []byte(`
<xs:simpleType name="x"></xs:simpleType>
`)
type Node struct {
XMLName xml.Name
Attrs []xml.Attr `xml:",any,attr"`
Content []byte `xml:",innerxml"`
Nodes []Node `xml:",any"`
}
func (n *Node) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
n.Attrs = start.Attr
type node Node
return d.DecodeElement((*node)(n), amp;start)
}
func (n *Node) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
n.Attrs = start.Attr
type node Node
return e.EncodeElement((*node)(n), start)
}
func main() {
buf := bytes.NewBuffer(data)
dec := xml.NewDecoder(buf)
var n Node
dec.Decode(amp;n)
output, _ := xml.Marshal(n)
fmt.Println(string(output))
}
Вывод:
<simpleType xmlns="xs" name="x" name="x"></simpleType>
Комментарии:
1. Вам не нужна пользовательская логика маршалинга или отмены маршалинга, и вы можете принудительно использовать это поведение, если перед маршалингом вы объедините пространство имен и имя тега: Go Playground . Отвечает ли это на ваш вопрос?
2. @icza спасибо за совет. Но если я попытаюсь использовать этот XML play.golang.org/p/ECIeA35BOpW — входящий и распечатанный XML отличаются. Почему?
3. Потому что в моем примере не изменились имена дочерних узлов. Вы должны сделать это рекурсивно. Смотрите мой ответ ниже.
Ответ №1:
Вам не нужна пользовательская логика маршалинга или отмены маршалинга, и вы можете принудительно использовать это поведение, если перед маршалингом вы объедините пространство имен и имя тега:
n.XMLName.Local = n.XMLName.Space ":" n.XMLName.Local
n.XMLName.Space = ""
Пример ввода соответствует выводу (попробуйте это на игровой площадке Go):
<xs:simpleType name="x" other="y">Some content</xs:simpleType>
Конечно, если есть дочерние теги, вам придется рекурсивно обрабатывать имена всех узлов. Давайте напишем помощник для этого:
func setName(n *Node) {
n.XMLName.Local = n.XMLName.Space ":" n.XMLName.Local
n.XMLName.Space = ""
for i := range n.Nodes {
setName(amp;n.Nodes[i])
}
}
Пример ввода XML:
<xs:simpleType name="comma-separated-integer">
<xs:annotation>
<xs:documentation>The comma-separated-integer element.</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:token">
<xs:pattern value="[1-9][0-9]*(, ?[1-9][0-9]*)*"/>
</xs:restriction>
</xs:simpleType>
Использование помощника:
buf := bytes.NewBuffer(data)
dec := xml.NewDecoder(buf)
var n Node
if err := dec.Decode(amp;n); err != nil {
panic(err)
}
setName(amp;n)
output, err := xml.Marshal(n)
fmt.Println(string(output))
if err != nil {
panic(err)
}
Тогда выходные данные почти совпадают с входными. Попробуйте это на игровой площадке Go.
Проблема в том, что внутреннее содержимое дублируется. Это потому, что Node.Content
это внутреннее содержимое XML, и дочерние теги также добавляются в Node.Nodes
качестве дочерних узлов. Таким образом, чтобы получить тот же результат, дочерние узлы не нужно маршалировать, Content
они уже содержат дочерние узлы.
Удалите дублирование, очистив Nodes
:
n.XMLName.Local = n.XMLName.Space ":" n.XMLName.Local
n.XMLName.Space = ""
n.Nodes = nil
Попробуйте это на игровой площадке Go.
Примечание: если вы собираетесь манипулировать дочерними узлами XML, это неприемлемый вариант. Затем вы можете сохранить setName()
вспомогательную функцию и, чтобы устранить дублирование, вы можете обнулить Content
узлы, у которых есть дочерние узлы:
func setName(n *Node) {
n.XMLName.Local = n.XMLName.Space ":" n.XMLName.Local
n.XMLName.Space = ""
if len(n.Nodes) != 0 {
n.Content = nil
}
for i := range n.Nodes {
setName(amp;n.Nodes[i])
}
}
Попробуйте это на игровой площадке Go.
Комментарии:
1. Я думаю, вам нужно добавить: if len(n.Nodes) != 0 {n.Content = nil} в функцию setName, потому что содержимое <xs:documentation> отсутствует. play.golang.org/p/_K7IvGakL9B
2. @nrw2000 Да, вы правы, работал над этим. Смотрите отредактированный ответ.
Ответ №2:
В нашей команде мы уже несколько месяцев используем фантастический модуль, который выражает XML, вдохновленный Python etrees.
https://github.com/beevik/etree
Это простой, но очень мощный инструмент для интерпретации XML-файлов и управления ими.