从XML模板生成XML文档

时间:2018-03-16 05:58:17

标签: xml go

我有一个像这样的xml模板:

<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://service.receive.appservice.jcms.hanweb.com">
   <soapenv:Header/>
   <soapenv:Body>
      <ser:wsGetInfosLink soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
         <nCataId xsi:type="xsd:int">3</nCataId>
         <bRef xsi:type="xsd:int">3</bRef>
         <nStart xsi:type="xsd:int">3</nStart>
         <nEnd xsi:type="xsd:int">3</nEnd>
         <bAsc xsi:type="xsd:int">3</bAsc>
         <strStartCTime xsi:type="xsd:string">gero et</strStartCTime>
         <strEndCTime xsi:type="xsd:string">sonoras imperio</strEndCTime>
         <strLoginId xsi:type="xsd:string">quae divum incedo</strLoginId>
         <strPwd xsi:type="xsd:string">verrantque per auras</strPwd>
         <strKey xsi:type="xsd:string">per auras</strKey>
      </ser:wsGetInfosLink>
   </soapenv:Body>
</soapenv:Envelope>

我需要生成相同格式的xml文档,将wsGetInfosLink子元素值更改为从客户端接收的某些值。因为xml模板有很多格式。我必须解析xml并以动态方式生成,我怎样才能实现它golang?

现在我可以动态地使用xml.Encoder.Token解析xml模板,但我不知道如何激活值并生成新的xml文档。

func TestOperation_DoRequest(t *testing.T) {

xmlStr := ""

var xmlTemplate bytes.Buffer
xmlTemplate.Write([]byte(xmlStr))
decoder := xml.NewDecoder(&xmlTemplate)
root, err := decoder.Token()
if err != nil {
    t.Fatal(err)
}
for t := root; err == nil; t, err = decoder.Token() {
    switch  t.(type) {
    case xml.StartElement:
        token := t.(xml.StartElement)
        fmt.Println("len len len", len(token.Attr))
        if len(token.Attr) > 0 {
            for _, attr := range token.Attr {
                attrName := attr.Name.Local
                attrSpace := attr.Name.Space
                attrValue := attr.Value
                fmt.Printf("attrSpace:%s attrName:%s attrValue:%s\n", attrSpace, attrName, attrValue)
            }
        }

        name := token.Name.Local
        space := token.Name.Space
        fmt.Printf("Token space:%s name:%s\n", space, name)
        break
    case xml.EndElement:
        token := t.(xml.EndElement)
        //element := t.(xml.EndElement)
        name := token.Name.Local
        fmt.Printf("end element name:%s\n", name)
        break
    case xml.CharData:
        token := t.(xml.CharData)
        content := string([]byte(token))

        fmt.Printf("---value %s\n", content)
        break
    default:
        break
    }
}

}

1 个答案:

答案 0 :(得分:1)

解析某些XML,修改其值,然后生成带有修改的新XML的常用方法是定义匹配 XML结构的类型,然后使用{{ 1}}您将XML解码为该类型的值,修改该值,然后使用xml.Unmarshal将该修改后的值编码回XML。

xml.Marshal

现在使用SOAP的XML及其前缀会变得有点复杂。

据我所知,var data = []byte(`<person> <name>John Doe</name> </person>`) type Person struct { XMLName xml.Name `xml:"person"` Name string `xml:"name"` } func main() { person := new(Person) // unmashal xml data into person if err := xml.Unmarshal(data, person); err != nil { panic(err) } // modify person person.Name = "Jane Doe" // marshal modified person back into xml b, err := xml.Marshal(person) if err != nil { panic(err) } fmt.Pritnln(string(b)) } 包不提供对XML前缀的直接支持,对问题#9519的讨论似乎证实了这一点。我建议您仔细阅读并重新考虑您将用于解决问题的工具。

也就是说,在已经提到的讨论中,有一个建议是使用两种不同的类型,一种用于解组,另一种用于编组XML数据,这样你可以能够实现你的目标,如果您喜欢这个想法,那么示例相当简单,您应该能够使用它们来指导您完整实现。

除了两种类型的解决方案之外,还会想到另一种方法,但是,虽然它适用于这一小块SOAP数据,但我没有任何真正的SOAP经验可以说明这是否足够工作使用实际的SOAP API。

这里的想法是利用encoding/xmlxml.Marshaler接口。每个具有前缀的XML元素,例如xml.MarshalerAttr需要一个匹配类型来实现Marshaler接口和每个具有前缀的属性,例如需要将<soapenv:Envelope>...声明为具有实现MarshalerAttr接口的类型的struct字段。

带前缀的示例元素:

soapenv:encodingStyle="...

带前缀的示例属性:

type Envelope struct {
    XMLName xml.Name  `xml:"Envelope"`
    // ...
}

// MarhsalXML implements the xml.Marshaler interface.
func (env *Envelope) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
    start.Name.Local = "soapenv:" + start.Name.Local
    return e.EncodeElement(*env, start)
}

您可以在此处找到更完整的示例:https://play.golang.org/p/4wba60hBv7I