Escape unicode characters in Go JSON so the output matches Python - json

In Python 2.7, if I encode JSON I get unicode-escaped strings:
>>> import json
>>> s = {"text": "三杯雞"}
>>> print(json.dumps(s))
it gives this output:
{"text": "\u4e09\u676f\u96de"}
But in Go, similar code:
package main
import (
"encoding/json"
"fmt"
)
type Food struct {
Name string `json:"name"`
}
func main() {
food := Food{Name: "三杯雞"}
v, _ := json.Marshal(food)
fmt.Println(string(v))
}
Gives this:
{"name":"三杯雞"}
The Chinese characters are not escaped. I am porting API endpoints from Python to Go - how can I get it to have the same escaped output as Python?
I tried variations using strconv.QuoteToASCII, but they result in the unicode being double-escaped:
func main() {
s := strconv.QuoteToASCII("三杯雞")
s = strings.Trim(s, "\"")
food := Food{Name: s}
v, _ := json.Marshal(food)
fmt.Println(string(v))
}
Outputs:
{"name":"\\u4e09\\u676f\\u96de"}

One solution is to use the strconv.QuoteToASCII method inside of a custom JSON marshaler:
package main
import (
"encoding/json"
"fmt"
"strconv"
)
type Food struct {
Name utf8String `json:"name"`
}
type utf8String string
func (s utf8String) MarshalJSON() ([]byte, error) {
return []byte(strconv.QuoteToASCII(string(s))), nil
}
func main() {
food := Food{Name: utf8String("三杯雞")}
v, _ := json.Marshal(food)
fmt.Println(string(v))
}
Output:
{"name":"\u4e09\u676f\u96de"}
This has the drawback that you can't use a plain string type in the struct definition, but the final output is ASCII-quoted, just like in Python.

Related

Unmarshal JSON to Struct of Map

I have below program. I need to convert JSON data into object type cache which contains a single field of map[string]string type. There is something that I am doing wrong w.r.t. initialising map and unmarshalling JSON but unable to identify the syntax issue.
Note: I have marshalled data just for convenience sake to have sample JSON data.
package main
import (
"fmt"
"encoding/json"
"strconv"
)
const data = `{"Mode":{"ID-1":"ON","ID-2":"OFF","ID-3":"ON"}}`
type Cache struct {
Mode map[string]string `json:"Mode"`
}
func main() {
jsonData, _ := json.Marshal(data)
fmt.Println(strconv.Unquote(string(jsonData)))
var c Cache
c.Mode = make(map[string]string) //I want to initialise map so that I can store data in next step, but this is wrong I know
c.Mode["ID-4"] = "ON" //Want to store data like this
json.Unmarshal(jsonData, &c)
fmt.Println(c) //I am getting output as nil map i.e. {map[]}
for k, v := range c.Mode {
fmt.Println(k, v) //I am getting NO output i.e. blank
}
}
Here is your your code fixed https://play.golang.org/p/5ftaiz_Q5wl (attaching blow the copy)
package main
import (
"encoding/json"
"fmt"
"log"
)
const data = `{"Mode":{"ID-1":"ON","ID-2":"OFF","ID-3":"ON"}}`
type Cache struct {
Mode map[string]string `json:"Mode"`
}
func main() {
c := Cache{}
err := json.Unmarshal([]byte(data), &c)
if err != nil {
log.Println(err)
}
c.Mode["ID-4"] = "ON" //Want to store data like this
fmt.Println(c)
for k, v := range c.Mode {
fmt.Println(k, v)
}
}
Here is the output:
{map[ID-1:ON ID-2:OFF ID-3:ON ID-4:ON]}
ID-2 OFF
ID-3 ON
ID-4 ON
ID-1 ON

Golang equivalent to Python json.dumps and json.loads

This is a very weird situation but I need to convert a stringified json to something valid that I can unmarshall with:
"{\"hello\": \"hi\"}"
I want to be able to unmarshall this into a struct like this:
type mystruct struct {
Hello string `json:"hello,string"`
}
I know normally the unmarshall takes bytes but Im trying to convert what I currently get into something structified.
Any suggestions?
The issue is that the encoding/json package accepts well-formed JSON, in this case the initial JSON that you have has escaped quotes, first you have to unescape them, one way to do this is by using the strconv.Unquote function, here's a sample snippet:
package main
import (
"encoding/json"
"fmt"
"strconv"
)
type mystruct struct {
Hello string `json:"hello,omitempty"`
}
func main() {
var rawJSON []byte = []byte(`"{\"hello\": \"hi\"}"`)
s, _ := strconv.Unquote(string(rawJSON))
var val mystruct
if err := json.Unmarshal([]byte(s), &val); err != nil {
// handle error
}
fmt.Println(s)
fmt.Println(err)
fmt.Println(val.Hello)
}

Golang Json Unmashalling to structure containing interface{}

I have a json unmarshal requirement to a struct containing an interface{}. The inner type is known only at runtime, hence the struct is defined with interface{}. However, the type is populated correctly before passing to json.Unmarshal. But the json.Unmarshal always populates the resulting struct with map[] instead of given type.
How do I resolve this to get the correct unmarshal behavior here.
See the simple test code and behavior here :
package main
import (
"fmt"
// s "strings"
"encoding/json"
)
type one struct {
S string `json:"status"`
Res interface{} `json:"res"`
}
type two struct {
A string
B string
}
func main() {
t := []two {{A:"ab", B:"cd",}, {A:"ab1", B:"cd1",}}
o := one {S:"s", Res:t}
js, _ := json.Marshal(&o)
fmt.Printf("o: %+v\n", o)
fmt.Printf("js: %s\n", js)
er := json.Unmarshal(js, &o)
fmt.Printf("er: %+v\n", er)
fmt.Printf("o: %+v\n", o)
}
Result:
o: {S:s Res:[{A:ab B:cd} {A:ab1 B:cd1}]}
js: {"status":"s","res":[{"A":"ab","B":"cd"},{"A":"ab1","B":"cd1"}]}
er: <nil>
o: {S:s Res:[map[A:ab B:cd] map[B:cd1 A:ab1]]}
See Code here1
You could create an ancillary one type that holds the expected type. The convert from that ancillary type back to the regular one type.
i.e. (play link):
package main
import (
"fmt"
// s "strings"
"encoding/json"
)
type one struct {
S string `json:"status"`
Res interface{} `json:"res"`
}
type two struct {
A string
B string
}
type oneStatic struct {
S string `json:"status"`
Res []two `json:"res"`
}
func main() {
t := []two{{A: "ab", B: "cd"}, {A: "ab1", B: "cd1"}}
o := one{S: "s", Res: t}
js, _ := json.Marshal(&o)
fmt.Printf("o: %+v\n", o)
fmt.Printf("js: %s\n", js)
var oStatic oneStatic
er := json.Unmarshal(js, &oStatic)
fmt.Printf("er: %+v\n", er)
o = one{oStatic.S, oStatic.Res}
fmt.Printf("o: %+v\n", o)
}

golang convert array of interfaces to strings

I read JSON data from a remote source and convert it to a map. There's some array in the data of which I want to examine the string values. After converting I think m["t"] is an array of interfaces. fmt.Print converts this to printed text on the console but I cannot figure a way to do a simple string comparison like
if val[0] == "str-c" {fmt.Println("success")}
How do I iterate through that and do string comparisons?
package main
import (
"fmt"
"encoding/json"
)
func main() {
var m map[string]interface{}
sJSON := `{"k": "v", "t":["str-a","str-b","str-c"]}`
_ = json.Unmarshal([]byte(sJSON),&m)
// find out if one of the string values of "t" is "str-b"
fmt.Println(m["t"])
}
m["t"] is of type interface{} and is the full array, if you wanted to get str-b it is at index one and you have to do some type assertion to get it as a string. Here's an example; https://play.golang.org/p/W7ZnMgicc7
If you want to check for it in the collection that would look like this;
package main
import (
"fmt"
"encoding/json"
)
func main() {
var m map[string]interface{}
sJSON := `{"k": "v", "t":["str-a","str-b","str-c"]}`
_ = json.Unmarshal([]byte(sJSON),&m)
// find out if one of the string values of "t" is "str-b"
for _, v := range m["t"].([]interface{}) {
if v.(string) == "str-b" {
fmt.Println("match found!")
}
}
//fmt.Println(m["t"].([]interface{})[1].(string))
}
https://play.golang.org/p/vo_90bKw92
If you want to avoid this 'unboxing' stuff, which I would recommend you do, you could instead define a struct to unmarshal into, itwould look like this;
type MyStruct struct {
K string `json:"k"`
T []string `json:"t"`
}
Then you can just range over T without any type assertions and do the compare, working example here; https://play.golang.org/p/ehPxOygGf5

Go: Convert Strings Array to Json Array String

Trying to convert a strings array to a json string in Go. But all I get is an array of numbers.
What am I missing?
package main
import (
"fmt"
"encoding/json"
)
func main() {
var urls = []string{
"http://google.com",
"http://facebook.com",
"http://youtube.com",
"http://yahoo.com",
"http://twitter.com",
"http://live.com",
}
urlsJson, _ := json.Marshal(urls)
fmt.Println(urlsJson)
}
Code on Go Playground: http://play.golang.org/p/z-OUhvK7Kk
By marshaling the object, you are getting the encoding (bytes) that represents the JSON string. If you want the string, you have to convert those bytes to a string.
fmt.Println(string(urlsJson))
Another way is to use directly os.Stdout.Write(urlsJson)
You could use stdout output encoder:
package main
import (
"encoding/json"
"os"
)
func main() {
json.NewEncoder(os.Stdout).Encode(urls)
}
or a string builder:
package main
import (
"encoding/json"
"strings"
)
func main() {
b := new(strings.Builder)
json.NewEncoder(b).Encode(urls)
print(b.String())
}
https://golang.org/pkg/encoding/json#NewEncoder