user3366304
user3366304

Reputation: 43

Golang: structure to generate/parse both XML and JSON

The use case is to generate (and parse) the following XML and JSON not creating separate structures for every of them.

XML

<xxx xmlns="http://example.org/ns">
    <data type="plaintext">Hello</data>

    <field1>Something1</field1>
    <field2>Something2</field2>
    ...
</xxx>

JSON

{
    "data": "Hello",
    "data_type": "plaintext",

    "field1": "Something1",
    "field2": "Something2"
    ...
}

Possible solution would be:

type Xxx struct {
    XMLName xml.Name `xml:"http://example.org/ns xxx" json:"-"`

    // **If only "inline" attribute had existed**
    Data    Data     `xml:"data" json:",inline"`

    // There are a lot of other fields here
    Field1  string   `xml:"field1" json:"field1"`
    Field2  string   `xml:"field2" json:"field2"`
    ...
}

type Data struct {
    Value string `xml:",chardata" json:"data"`
    Type  string `xml:"type,attr" data_type:"data"`
}

However, right now it produces the following JSON:

{"Data": {"data": "Hello", "type": "plaintext"}, "field1": "Something1", "field2": "Something2"}

which is not what I need. So, is there any other way to solve the problem not using separate structures for xml and json?

Upvotes: 3

Views: 1888

Answers (1)

Elwinar
Elwinar

Reputation: 9509

The solution is to write a custom MarshalJSON (or MarshalXML) and UnmarshalJSON (or UnmarshalXML) to handle the differences in your text-representations. Example:

func (x *Xxx) MarshalJSON() ([]byte, error) {
    return []byte(`{"data": "` + x.Data.Value + `","type":"` + x.Data.Type + `","field1":"` + x.Field1 + `","field2":"` + x.Field2 + `"}`), nil
}

This example is a rough one to demonstrate the principle. For something more efficient, you should use another structure to handle the process. This structure doesn't need to be exported (in fact you don't want to export it), just to be there and used by your function. Example:

type xxxJSON struct {
    Data string
    Type string
    Field1 string
    Field2 string
}

func (x *Xxx) MarshalJSON() ([]byte, error) {
    out := xxxJSON{}
    out.Data = x.Data.Value
    out.Type = x.Data.Type
    out.Field1 = x.Field1
    out.Field2 = x.Field2
    return json.Marshal(out)
}

Upvotes: 2

Related Questions