Parsing dynamic XML

2,579 views
Skip to first unread message

pic...@gmail.com

unread,
Nov 14, 2013, 2:57:51 AM11/14/13
to golan...@googlegroups.com
I'm trying to marshal and unmarshal a SOAP request that can have many different <Body> child tags.  My thought was to do something like this:

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

type Body struct {
        XMLName xml.Name `xml:"http://www.w3.org/2003/05/soap-envelope Body"`
        Content interface{} `xml:",any"
}

type OneResult struct {
        SomeValue string
}

Then *maybe* I could teach it how to unmarshal by assigning an object to Content, like so:

result := &OneResult{}
envelope := &Envelope{Body: &Body{Content: result}}
xml.Unmarshal(body, envelope)
fmt.Printf("Result = %#v\n", result)

So that if my SOAP request returned a body like this (I'm leaving out all the namespaces and attributes for brevity):

<Envelope>
    <Body>
        <OneResult>
            <Value>Some Value</Value>
        </OneResult>
    </Body>
</Envelope>

I'd print out a instance of the OneResult object with a Value of "Some Value" inside.  But this doesn't work.

I'm doing this because there could be hundreds of different SOAP responses, and I don't want a Body that looks like this:

type Body struct {
        OneResult *OneResult `xml:",omitempty"`
        TwoResult *OneResult `xml:",omitempty"`
        ThreeResult *OneResult `xml:",omitempty"`
        FourResult *OneResult `xml:",omitempty"`
        FiveResult *OneResult `xml:",omitempty"`
        SixResult *OneResult `xml:",omitempty"`
        SevenResult *OneResult `xml:",omitempty"`
        ....
}

Is there any way to accomplish this with the Go XML unmarshaler?


pic...@gmail.com

unread,
Nov 14, 2013, 3:00:41 AM11/14/13
to golan...@googlegroups.com, pic...@gmail.com
Sorry, this should have been:

<Envelope>
    <Body>
        <OneResult>
            <SomeValue>Some Value</SomeValue>
        </OneResult>
    </Body>
</Envelope>

Mateusz Czapliński

unread,
Nov 14, 2013, 7:47:44 AM11/14/13
to golan...@googlegroups.com
On Thursday, November 14, 2013 8:57:51 AM UTC+1, pic...@gmail.com wrote:
I'm doing this because there could be hundreds of different SOAP responses, and I don't want a Body that looks like this:

type Body struct {
        OneResult *OneResult `xml:",omitempty"`
        TwoResult *OneResult `xml:",omitempty"`
        ThreeResult *OneResult `xml:",omitempty"`
        FourResult *OneResult `xml:",omitempty"`
        FiveResult *OneResult `xml:",omitempty"`
        SixResult *OneResult `xml:",omitempty"`
        SevenResult *OneResult `xml:",omitempty"`
        ....
}

Is there any way to accomplish this with the Go XML unmarshaler?

I'm not sure if I understand your problem correctly and enough, so sorry if my answer will be misguided, but two thoughts occurred to me, which might or might not help you:

1. If OneResult, TwoResult, etc are similar enough, it might be possible to create a CommonResult, with fields being union of the other structs' fields. Then, you could add an xml.Name field, which should get filled with the name of the actual node (for example, string "ThreeResult").

2. Otherwise, I think I've read there's some attempt at introducing custom (un-)marshallers in pkg "encoding" in Go tip[1], reportedly respected by "encoding/xml". That said, after a (very) quick glance at docs for "encoding/xml" on tip [2], I didn't manage to find more info on how to do that, and I can't say if that would allow to solve your problem.

3. As a fallback, you could always try to build something custom on top of encoding/xml.Decoder.Token().

  [1]: http://tip.golang.org/pkg/encoding/
  [2]: http://tip.golang.org/pkg/encoding/xml

/Mateusz.

C Banning

unread,
Nov 14, 2013, 8:05:34 AM11/14/13
to golan...@googlegroups.com, pic...@gmail.com

kcf...@gmail.com

unread,
Sep 23, 2014, 11:05:05 AM9/23/14
to golan...@googlegroups.com, pic...@gmail.com
I'm attempting to do the same thing. Did you ever find a solution? I really want to avoid defining a Body struct with many (over 100) possible SOAP responses.

Sean Bowman

unread,
Sep 23, 2014, 3:45:33 PM9/23/14
to kcf...@gmail.com, golan...@googlegroups.com
For one app, I just sucked it up and created the extra elements in the Body struct.  Nowhere near the 100 you’re talking about.  I only had two or three.

For another app that did have a large number of possible request (I can’t remember how many, but it was an entire API in SOAP), I cheated.  I created a special io.Reader that hacked the Body content out of the XML using a simply byte array parsing state machine, so the byte array going to the unmarshaler was just the XML from within the <Body> tag.  Basically ignored all the SOAP wrappings.

But I feel like the “real” way to do it is a Decoder.  I haven’t tried it, but my guess is that it’s designed for this situation.
Reply all
Reply to author
Forward
0 new messages