Go,encoding / xml:我如何编组自闭元素?

时间:2016-06-30 08:44:57

标签: xml go

我从以下结构中编写XML:

type OrderLine struct {
    LineNumber     string `xml:"LineNumber"`
    Product        string `xml:"Product"`
    Ref            string `xml:"Ref"`
    Quantity       string `xml:"Quantity"`
    Price          string `xml:"Price"`
    LineTotalGross string `xml:"LineTotalGross"`
}

如果Ref字段为空,我想要显示的元素,但是要自动关闭,即

<Ref />

<Ref></Ref>

AFAIK,这两个在语义上是等价的,但我更喜欢自动关闭标签,因为它匹配其他系统的输出。这可能吗?

2 个答案:

答案 0 :(得分:1)

我找到了一种方法来做“黑客”元帅包,但我没有测试它。如果您希望我向您显示链接,请立即告诉我,然后将其发布在此回复的评论中。

我做了一些手动代码:

package main

import (
    "encoding/xml"
    "fmt"
    "regexp"
    "strings"
)

type ParseXML struct {
    Person struct {
        Name     string `xml:"Name"`
        LastName string `xml:"LastName"`
        Test     string `xml:"Abc"`
    } `xml:"Person"`
}

func main() {

    var err error
    var newPerson ParseXML

    newPerson.Person.Name = "Boot"
    newPerson.Person.LastName = "Testing"

    var bXml []byte
    var sXml string
    bXml, err = xml.Marshal(newPerson)
    checkErr(err)

    sXml = string(bXml)

    r, err := regexp.Compile(`<([a-zA-Z0-9]*)><(\\|\/)([a-zA-Z0-9]*)>`)
    checkErr(err)
    matches := r.FindAllString(sXml, -1)

    fmt.Println(sXml)

    if len(matches) > 0 {
        r, err = regexp.Compile("<([a-zA-Z0-9]*)>")
        for i := 0; i < len(matches); i++ {

            xmlTag := r.FindString(matches[i])
            xmlTag = strings.Replace(xmlTag, "<", "", -1)
            xmlTag = strings.Replace(xmlTag, ">", "", -1)
            sXml = strings.Replace(sXml, matches[i], "<"+xmlTag+" />", -1)

        }
    }

    fmt.Println("")
    fmt.Println(sXml)

}

func checkErr(chk error) {
    if chk != nil {
        panic(chk)
    }
}

答案 1 :(得分:0)

这篇文章提供了两种不依赖于正则表达式的解决方案,并解释了两者之间的区别。

第一个版本对内存友好,但对 CPU 不利。它实现了一个编写器,通过在缓冲字节内替换来替换搜索的出现。它尝试尽快写入数据,防止内存中的大量分配。 这不是 CPU 的最佳用法,因为 if 会多次扫描相同的数据。

package main

import (
    "bytes"
    "encoding/xml"
    "fmt"
    "io"
    "os"
)

// Person represents a <person> node in the XML
type Person struct {
    XMLName   xml.Name   `xml:"Players"`
    DataItems []dataItem `xml:"DataItem"`
}

// Skill represents a <skill> node in the XML
type dataItem struct {
    XMLName        xml.Name `xml:"DataItem"`
    Name           string   `xml:"skillName,attr"`
    YearsPracticed int64    `xml:"practice,attr"`
    Level          string   `xml:"level,attr"`
}

func main() {
    players := Person{
        DataItems: []dataItem{
            {Name: "Soccer", YearsPracticed: 3, Level: "Newbie"},
            {Name: "Basketball", YearsPracticed: 4, Level: "State"},
            {Name: "Baseball", YearsPracticed: 10, Level: "National"},
        },
    }
    players.DataItems = append(players.DataItems, players.DataItems...)
    players.DataItems = append(players.DataItems, players.DataItems...)
    players.DataItems = append(players.DataItems, players.DataItems...)
    players.DataItems = append(players.DataItems, players.DataItems...)
    players.DataItems = append(players.DataItems, players.DataItems...)

    dst := &Replace{
        Writer:  os.Stdout,
        Search:  []byte("></DataItem>"),
        Replace: []byte("/>"),
    }
    defer dst.Flush()

    enc := xml.NewEncoder(dst)
    enc.Indent("", "  ")
    if err := enc.Encode(players); err != nil {
        fmt.Printf("error: %v\n", err)
    }
}

type Replace struct {
    io.Writer
    Search  []byte
    Replace []byte
    buf     []byte
}

func (s *Replace) Write(b []byte) (n int, err error) {
    s.buf = append(s.buf, b...)
    s.buf = bytes.ReplaceAll(s.buf, s.Search, s.Replace)
    if len(s.buf) > len(s.Search) {
        w := s.buf[:len(s.buf)-len(s.Search)]
        n, err = s.Writer.Write(w)
        s.buf = s.buf[n:]
    }
    return len(b), err
}

func (s *Replace) Flush() (err error) {
    var n int
    n, err = s.Writer.Write(s.buf)
    s.buf = s.buf[n:]
    return
}

第二个版本对 cpu 友好但内存不利,因为它加载整个数据在内存中修改。

package main

import (
    "bytes"
    "encoding/xml"
    "fmt"
    "os"
)

// Person represents a <person> node in the XML
type Person struct {
    XMLName   xml.Name   `xml:"Players"`
    DataItems []dataItem `xml:"DataItem"`
}

// Skill represents a <skill> node in the XML
type dataItem struct {
    XMLName        xml.Name `xml:"DataItem"`
    Name           string   `xml:"skillName,attr"`
    YearsPracticed int64    `xml:"practice,attr"`
    Level          string   `xml:"level,attr"`
}

func main() {
    players := Person{
        DataItems: []dataItem{
            {Name: "Soccer", YearsPracticed: 3, Level: "Newbie"},
            {Name: "Basketball", YearsPracticed: 4, Level: "State"},
            {Name: "Baseball", YearsPracticed: 10, Level: "National"},
        },
    }
    players.DataItems = append(players.DataItems, players.DataItems...)
    players.DataItems = append(players.DataItems, players.DataItems...)
    players.DataItems = append(players.DataItems, players.DataItems...)
    players.DataItems = append(players.DataItems, players.DataItems...)
    players.DataItems = append(players.DataItems, players.DataItems...)

    out := new(bytes.Buffer)

    enc := xml.NewEncoder(out)
    enc.Indent("", "  ")
    if err := enc.Encode(players); err != nil {
        fmt.Printf("error: %v\n", err)
    }

    b := bytes.ReplaceAll(out.Bytes(), []byte("></DataItem>"), []byte("/>"))
    os.Stdout.Write(b)
}

根据您的执行环境选择一个。