Home > Software design >  How to unmarshal dynamic XML data to struct?
How to unmarshal dynamic XML data to struct?

Time:06-01

I have a XML data which has some repeated data with dynamic element name as shown below.

<Ht>
<Criteria>
    <ADNode_1>2</ADNode_1>
    <CDNode_1>2</CDNode_1>
    <IFNode_1>0</IFNode_1>
    <ADNode_2>2</ADNode_2>
    <CDNode_2>0</CDNode_2>
    <IFNode_2>0</IFNode_2>
    <ADNode_3>0</ADNode_3>
    <CDNode_3>0</CDNode_3>
    <IFNode_3>0</IFNode_3>
</Criteria>
<session id="1056134770841202228344907">
    <Htd ID="21170">
        <Data_1>
            <Info Count="2"></Info>
            <Data Id="iV29308/B2/R1">
                <Type>TR1</Type>
            </Data>
            <Data Id="iV29308/B3/R1">
                <Type>TR1</Type>
            </Data>
            <Data Id="iV29308/B4/R1">
                <Type>TR1</Type>
            </Data>
            <Data Id="iV29308/B6/R1">
                <Type>TR1</Type>
            </Data>
        </Data_1>
        <Data_2>
            <Info Count="2"></Info>
            <Data Id="iV29308/B2/R1">
                <Type>TR2</Type>
            </Data>
            <Data Id="iV29308/B3/R1">
                <Type>TR2</Type>
            </Data>
            <Data Id="iV29308/B4/R1">
                <Type>TR2</Type>
            </Data>
            <Data Id="iV29308/B6/R1">
                <Type>TR3</Type>
            </Data>
        </Data_2>
    </Htd>
</session>

I can create individual structs for <ADNode_1> ,<ADNode_2> and <ADNode_3> or <Data_1>, <Data_2> but there can be n number of these nodes. Like below

<ADNode_1>2</ADNode_1>
<ADNode_2>2</ADNode_2>
<ADNode_3>2</ADNode_3>
<ADNode_n>2</ADNode_n>

OR

<Data_1></Data_1>
<Data_2></Data_2>
<Data_3></Data_3>
<Data_n></Data_n>

How can I create struct for these nodes to have n number of nodes or elements?

Here is the playground link I am trying to work with.

CodePudding user response:

Generally for these situations you may use a slice in Go to "gather" the elements, and use the ,any option to put everything in it you don't have a mapping for. To be able to identify the source, use an XMLName xml.Name field which will retain the name of the XML tag it originates from.

For example you may model your XML like this:

type Ht struct {
    Criteria struct {
        Nodes []struct {
            XMLName xml.Name
            Content string `xml:",chardata"`
        } `xml:",any"`
    }
    Session struct {
        ID  string `xml:"id,attr"`
        Htd struct {
            ID    string `xml:"ID,attr"`
            DataX []struct {
                XMLName xml.Name
                Info    struct {
                    Count int `xml:"Count,attr"`
                }
                DataNodes []struct {
                    XMLName xml.Name
                    ID      string `xml:"Id,attr"`
                    Type    string
                } `xml:",any"`
            } `xml:",any"`
        }
    } `xml:"session"`
}

Parsing it and reencoding it will retain all elements:

var ht Ht
if err := xml.Unmarshal([]byte(src), &ht); err != nil {
    panic(err)
}

result, err := xml.MarshalIndent(ht, "", "  ")
if err != nil {
    panic(err)
}

fmt.Println(string(result))

This will output (try it on the Go Playground):

<Ht>
  <Criteria>
    <ADNode_1>2</ADNode_1>
    <CDNode_1>2</CDNode_1>
    <IFNode_1>0</IFNode_1>
    <ADNode_2>2</ADNode_2>
    <CDNode_2>0</CDNode_2>
    <IFNode_2>0</IFNode_2>
    <ADNode_3>0</ADNode_3>
    <CDNode_3>0</CDNode_3>
    <IFNode_3>0</IFNode_3>
  </Criteria>
  <session id="1056134770841202228344907">
    <Htd ID="21170">
      <Data_1>
        <Info Count="2"></Info>
        <Data Id="iV29308/B2/R1">
          <Type>TR1</Type>
        </Data>
        <Data Id="iV29308/B3/R1">
          <Type>TR1</Type>
        </Data>
        <Data Id="iV29308/B4/R1">
          <Type>TR1</Type>
        </Data>
        <Data Id="iV29308/B6/R1">
          <Type>TR1</Type>
        </Data>
      </Data_1>
      <Data_2>
        <Info Count="2"></Info>
        <Data Id="iV29308/B2/R1">
          <Type>TR2</Type>
        </Data>
        <Data Id="iV29308/B3/R1">
          <Type>TR2</Type>
        </Data>
        <Data Id="iV29308/B4/R1">
          <Type>TR2</Type>
        </Data>
        <Data Id="iV29308/B6/R1">
          <Type>TR3</Type>
        </Data>
      </Data_2>
    </Htd>
  </session>
</Ht>
  • Related