20

How can I read a json file into a struct, and then Marshal it back out to a json string with the Struct fields as keys (rather than the original json keys)?

(see Desired Output to Json File below...)

Code:

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
)

type Rankings struct {
    Keyword  string `json:"keyword"`
    GetCount uint32 `json:"get_count"`
    Engine   string `json:"engine"`
    Locale   string `json:"locale"`
    Mobile   bool   `json:"mobile"`
}

func main() {
    var jsonBlob = []byte(`
        {"keyword":"hipaa compliance form", "get_count":157, "engine":"google", "locale":"en-us", "mobile":false}
    `)
    rankings := Rankings{}
    err := json.Unmarshal(jsonBlob, &rankings)
    if err != nil {
        // nozzle.printError("opening config file", err.Error())
    }

    rankingsJson, _ := json.Marshal(rankings)
    err = ioutil.WriteFile("output.json", rankingsJson, 0644)
    fmt.Printf("%+v", rankings)
}

Output on screen:

{Keyword:hipaa compliance form GetCount:157 Engine:google Locale:en-us Mobile:false}

Output to Json File:

{"keyword":"hipaa compliance form","get_count":157,"engine":"google","locale":"en-us","mobile":false}

Desired Output to Json File:

{"Keyword":"hipaa compliance form","GetCount":157,"Engine":"google","Locale":"en-us","Mobile":false}
Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Joe Bergevin
  • 3,158
  • 5
  • 26
  • 34

3 Answers3

30

If I understand your question correctly, all you want to do is remove the json tags from your struct definition.

So:

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
)

type Rankings struct {
    Keyword  string 
    GetCount uint32 
    Engine   string 
    Locale   string 
    Mobile   bool   
}

func main() {
    var jsonBlob = []byte(`
        {"keyword":"hipaa compliance form", "get_count":157, "engine":"google", "locale":"en-us", "mobile":false}
    `)
    rankings := Rankings{}
    err := json.Unmarshal(jsonBlob, &rankings)
    if err != nil {
        // nozzle.printError("opening config file", err.Error())
    }

    rankingsJson, _ := json.Marshal(rankings)
    err = ioutil.WriteFile("output.json", rankingsJson, 0644)
    fmt.Printf("%+v", rankings)
}

Results in:

{Keyword:hipaa compliance form GetCount:0 Engine:google Locale:en-us Mobile:false}

And the file output is:

{"Keyword":"hipaa compliance form","GetCount":0,"Engine":"google","Locale":"    en-us","Mobile":false}

Running example at http://play.golang.org/p/dC3s37HxvZ

Note: GetCount shows 0, since it was read in as "get_count". If you want to read in JSON that has "get_count" vs. "GetCount", but output "GetCount" then you'll have to do some additional parsing.

See Go- Copy all common fields between structs for additional info about this particular situation.

Community
  • 1
  • 1
Momer
  • 3,158
  • 22
  • 23
  • 3
    Go 1.8 now allows you to copy straight over to a struct with identical field names, but different tag defs: "When explicitly converting a value from one struct type to another, as of Go 1.8 the tags are ignored. Thus two structs that differ only in their tags may be converted from one to the other" (see https://golang.org/doc/go1.8). So I would just have 2 structs: one for unmarshaling an API response and one for marshaling it how I want it. see adjusted playground: https://play.golang.org/p/lEdh1Fammj – Joe Bergevin Aug 09 '17 at 19:43
1

Try to change the json format in the struct

type Rankings struct {
    Keyword  string `json:"Keyword"`
    GetCount uint32 `json:"Get_count"`
    Engine   string `json:"Engine"`
    Locale   string `json:"Locale"`
    Mobile   bool   `json:"Mobile"`
}
boulavard
  • 11
  • 1
1

An accourance happened by just using json.Marshal() / json.MarshalIndent(). It overwrites the existing file, which in my case was suboptimal. I just wanted to add content to current file, and keep old content.

This writes data through a buffer, with bytes.Buffer type.

This is what I gathered up so far:

package srf

import (
    "bytes"
    "encoding/json"
    "os"
)

func WriteDataToFileAsJSON(data interface{}, filedir string) (int, error) {
    //write data as buffer to json encoder
    buffer := new(bytes.Buffer)
    encoder := json.NewEncoder(buffer)
    encoder.SetIndent("", "\t")

    err := encoder.Encode(data)
    if err != nil {
        return 0, err
    }
    file, err := os.OpenFile(filedir, os.O_RDWR|os.O_CREATE, 0755)
    if err != nil {
        return 0, err
    }
    n, err := file.Write(buffer.Bytes())
    if err != nil {
        return 0, err
    }
    return n, nil
}

This is the execution of the function, together with the standard json.Marshal() or json.MarshalIndent() which overwrites the file

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"

    minerals "./minerals"
    srf "./srf"
)

func main() {

    //array of Test struct
    var SomeType [10]minerals.Test

    //Create 10 units of some random data to write
    for a := 0; a < 10; a++ {
        SomeType[a] = minerals.Test{
            Name:   "Rand",
            Id:     123,
            A:      "desc",
            Num:    999,
            Link:   "somelink",
            People: []string{"John Doe", "Aby Daby"},
        }
    }

    //writes aditional data to existing file, or creates a new file
    n, err := srf.WriteDataToFileAsJSON(SomeType, "test2.json")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("srf printed ", n, " bytes to ", "test2.json")

    //overrides previous file
    b, _ := json.MarshalIndent(SomeType, "", "\t")
    ioutil.WriteFile("test.json", b, 0644)

}

Why is this useful? File.Write() returns bytes written to the file! So this is perfect if you want to manage memory or storage.

WriteDataToFileAsJSON() (numberOfBytesWritten, error)
Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
accnameowl
  • 21
  • 2