1

I'm using github.com/basgys/goxml2json for xml to json conversion. Below is the example code:

package main

import (
    "fmt"
    "strings"
    xj "github.com/basgys/goxml2json"
)

func main() {
    xml := strings.NewReader(`<?xml version="1.0" encoding="UTF-8"?>
    <osm version="0.6" generator="CGImap 0.0.2">
     <bounds minlat="54.0889580" minlon="12.2487570" maxlat="54.0913900" maxlon="12.2524800"/>
     <foo>bar</foo>
    </osm>`)

    json, err := xj.Convert(xml)
    if err != nil {
        panic("ERROR converting xml to json")
    }

    fmt.Println(json.String())
}

The output of the above code is:

  {
    "osm": {
      "-version": 0.6,
      "-generator": "CGImap 0.0.2",
      "bounds": {
        "-minlat": "54.0889580",
        "-minlon": "12.2487570",
        "-maxlat": "54.0913900",
        "-maxlon": "12.2524800"
      },
      "foo": "bar"
    }
  }

However, I am expecting the output like below as given by https://codebeautify.org/xmltojson/y2221f265:

{
  "osm": {
    "bounds": "",
    "foo": "bar"
  }
}

How to remove the keys starting with - from the JSON output? I do not know the structure of the data beforehand.

Avinash
  • 875
  • 6
  • 20
  • Do you need a generic converter like this library or do you know the structure of the date you wish to convert? If you know the structure, defining a custom struct with https://pkg.go.dev/encoding/xml#Unmarshal might be easiest. It seems this library has plugins which can help customize how to convert but it seems that ignoring attributres might not be that easy https://github.com/basgys/goxml2json/blob/master/plugins.go. – Dylan Reimerink Apr 14 '22 at 09:08
  • Hi @DylanReimerink, as per my requirement I do not know the structure beforehand. So I have to work with generic converter like this library. – Avinash Apr 14 '22 at 10:42

2 Answers2

2

I think this should do it. It is a modified version of the goxml2json.Convert func. Used the WithAttrPrefix to specify a custom prefix(in case you ever want to use a - at the start of your body).

Please note that this only works for the latest commit on the master branch, the v1.1.0 tag doesn't support plugins, so you have to go get it like so: go get github.com/basgys/goxml2json@master

RemoveAttr just recursively deletes all children with the given prefix. You could also do other modifications at this point.

package main

import (
    "bytes"
    "fmt"
    "strings"

    xj "github.com/basgys/goxml2json"
)

const prefix = "veryuniqueattrprefix-"

func main() {
    xml := strings.NewReader(`<?xml version="1.0" encoding="UTF-8"?>
    <osm version="0.6" generator="CGImap 0.0.2">
     <bounds minlat="54.0889580" minlon="12.2487570" maxlat="54.0913900" maxlon="12.2524800"/>
     <foo>bar</foo>
    </osm>`)

    // Decode XML document
    root := &xj.Node{}
    err := xj.NewDecoder(
        xml,
        xj.WithAttrPrefix(prefix),
    ).Decode(root)
    if err != nil {
        panic(err)
    }

    RemoveAttr(root)

    // Then encode it in JSON
    buf := new(bytes.Buffer)
    e := xj.NewEncoder(buf)
    err = e.Encode(root)
    if err != nil {
        panic(err)
    }

    fmt.Println(buf.String())
}

func RemoveAttr(n *xj.Node) {
    for k, v := range n.Children {
        if strings.HasPrefix(k, prefix) {
            delete(n.Children, k)
        } else {
            for _, n := range v {
                RemoveAttr(n)
            }
        }
    }
}

outputs:

{"osm": {"bounds": "", "foo": "bar"}}
Dylan Reimerink
  • 5,874
  • 2
  • 15
  • 21
  • Thank you @Dylan. I have one doubt, why have you declared `const prefix = "veryuniqueattrprefix-"` instead of `const prefix = "-"`. – Avinash Apr 18 '22 at 04:06
  • @Avinash I thought that might be nessesery to avoid conflicts, but seeing as using a - in xml tags is rare, this shouldn't be that big of an issue – Dylan Reimerink Apr 18 '22 at 09:18
1

Try this

package main

import (
    "encoding/json"
    "fmt"
    "strings"

    xj "github.com/basgys/goxml2json"
)

// ToObject - convert string to any given struct
func ToObject(value string, object interface{}) error {
    err := json.Unmarshal([]byte(value), &object)
    if err != nil {
        return err
    }
    return nil
}
func main() {
    xml := strings.NewReader(`<?xml version="1.0" encoding="UTF-8"?>
    <osm version="0.6" generator="CGImap 0.0.2">
     <bounds minlat="54.0889580" minlon="12.2487570" maxlat="54.0913900" maxlon="12.2524800"/>
     <foo>bar</foo>
    </osm>`)

    converter, err := xj.Convert(xml)
    if err != nil {
        panic("ERROR converting xml to json")
    }
    osmStruct := &ExampleStruct{}
    ToObject(converter.String(), &osmStruct)

    b, _ := json.Marshal(osmStruct)
    fmt.Println(string(b))
}

// ExampleStruct -
type ExampleStruct struct {
    Osm Osm `json:"osm"`
}

// Osm -
type Osm struct {
    Version   string `json:",omitempty"`
    Generator string `json:",omitempty"`
    Bounds    Bounds `json:"bounds"`
    Foo       string `json:"foo"`
}

// Bounds -
type Bounds struct {
    Minlat string `json:",omitempty"`
    Minlon string `json:",omitempty"`
    Maxlat string `json:",omitempty"`
    Maxlon string `json:",omitempty"`
}
Santosh Anand
  • 1,230
  • 8
  • 13
  • Hi @SantoshAnand, Thank you for your answer. But as per my requirement I do not know the structure beforehand. That is the reason I'm using a generic converter like this library. – Avinash Apr 14 '22 at 11:07