Show namespace prefix in xml.Marshall in go

Viewed 45

I'm working on generating a SOAP message(xml) from golang. I have the following example.

package main

import (
    "encoding/xml"
    "fmt"
)

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

func main() {
    envelope := Envelope{
        XMLName: xml.Name{
            Local: "soapenv",
            Space: "http://www.w3.org/2003/05/soap-envelope",
        },
    }
    res, _ := xml.Marshal(envelope)
    fmt.Print(string(res))
}

This program prints the output as follows

<Envelope></Envelope>

I want to get the output with namespace prefix as below,

<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope"></soapenv:Envelope>

Can someone help me to get my desired output??

2 Answers

You can add the xmlns attribute explicitly to your struct definition:

type Envelope struct {
    XMLName xml.Name `xml:"soapenv:Envelope"`
    SoapEnv string   `xml:"xmlns:soapenv,attr"`
}

and then to instantiate it:

e := Envelope{
    SoapEnv: "http://www.w3.org/2003/05/soap-envelope",
}

https://go.dev/play/p/LrKNg8PTK2A

Output:

<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope"></soapenv:Envelope>

See XML docs example for more XML customizations.

colm.anseo's answer will work for generating XML the way you want it to be. But the generated XML just looks like coming from a valid model, but it isn't. You won't be able to unmarshal that XML with the same struct definition.

I've been fiddling with (de-)serializing XMI the whole day and finally got a clue how it's supposed to work: you need to fully qualify every single node in your xml tags. That is the namespace (not just the prefix) needs to be put in front of the name defined in the tags:

type Envelope struct {
    XMLName xml.Name `xml:"http://www.w3.org/2003/05/soap-envelope Envelope"`
}

But still you will not get exactly what you'd like to have. It will most propably look like this:

<Envelope xmlns="http://www.w3.org/2003/05/soap-envelope"></Envelope>

But at least this is valid XML and should fit your use case (assuming other tools in your toolchain play nice). And you will be able to unmarshal XML successfully.

Question is, what happens if you do have more than one namespace or need a namespace on attributes. Well, same story, prefix all tags with the namespace url. Marshal() will sort out how to deal with it, e.g. creating namespace prefixes on the fly.

Example:

type XMI struct {
    XMLName xml.Name `xml:"http://schema.omg.org/spec/XMI/2.1 XMI"`
    Version string   `xml:"http://schema.omg.org/spec/XMI/2.1 version,attr"`
    Model   *Model   `xml:"http://www.eclipse.org/uml2/5.0.0/UML Model"`
}

This will generate XML like this:

<XMI xmlns="http://schema.omg.org/spec/XMI/2.1" xmlns:_="http://schema.omg.org/spec/XMI/2.1" _:version="2.1">
  <Model xmlns="http://www.eclipse.org/uml2/5.0.0/UML" _:id="a7e26bff-10a4-44f7-b225-80e079d812c8" name="model">
    <packagedElement _:id="7c141da5-fccf-4da8-8fb8-53bf550f2cfc" xmlns:_XMLSchema-instance="http://www.w3.org/2001/XMLSchema-instance" _XMLSchema-instance:type="uml:PrimitiveType" name="char"></packagedElement>
  </Model>
</XMI>

As you can see, Marshal() will generate default namespaces for Elements and (computed) namespace prefixes for attributes when required. Valid XML, but hard to read I have to admit.

In contrast to others saying golang's encoding/xml is broken, I think it's just... inconvenient.

Related