Unmarshalling array into struct - go

I'm trying to figure out how I can (using gin) create a struct from an api call
"icon": [
"https://api.figo.me/assets/images/accounts/postbank.png",
{
"48x48": "https://api.figo.me/assets/images/accounts/postbank_48.png",
"60x60": "https://api.figo.me/assets/images/accounts/postbank_60.png",
"72x72": "https://api.figo.me/assets/images/accounts/postbank_72.png",
"84x84": "https://api.figo.me/assets/images/accounts/postbank_84.png",
"96x96": "https://api.figo.me/assets/images/accounts/postbank_96.png",
"120x120": "https://api.figo.me/assets/images/accounts/postbank_120.png",
"144x144": "https://api.figo.me/assets/images/accounts/postbank_144.png",
"192x192": "https://api.figo.me/assets/images/accounts/postbank_192.png",
"256x256": "https://api.figo.me/assets/images/accounts/postbank_256.png"
}
],
into
type CatalogBank struct {
Advice string `json:"advice"`
BankCode string `json:"bank_code"`
BankName string `json:"bank_name"`
BIC string `json:"bic"`
Credentials []struct {
Label string `json:"label"`
Masked bool `json:"masked"`
} `json:"credentials"`
Icon []struct {
} `json:"icon"`
Language []byte `json:"language"`
}
The icon part is just an extract from, but I always get an unmarshall error for this part. How would I have to definde the 'Icon' part in the struct?

This would work
package main
type CatalogBank struct {
Icon []interface{} `json:"icon"`
}

This is a little tricky in Golang because of the non-strict type in the JSON. If that is definitely the format you are going to receive the data in, you should unmarshal to an Interface{} and then parse the interface into a struct that you can use in your Golang
Direct Unmarshalling cannot be done, as the type of each field is not known
type Icon struct{
ImageLink string
ImageLink48 string
// ...
}
type CatalogBank struct {
Advice string `json:"advice"`
IconRaw []interface{} `json:"icon"`
Icon []Icon
//...
}
func UnmarshalIcon(c &CatalogBank, i interface{}):
// first convert it to the top level list
newIcon := Icon{}
listOfIcons := i.([]interface{})
for _, i := range listOfIcons:
switch iT := i.(type) {
case string:
newIcon.ImageLink = iT
case map[string]interface{}:
for smallIconsKey, smallIconLink := range iT {
if smallIconsKey == "48x48"{
newIcon.ImageLink48 = smallIconLink.(string)
}
// and so on
}
var c CatalogBank{}
_ := json.Unmarshal([]byte(your_json), &c)
for _, i := range c.IconRaw:
UnmarshalIcon(&c, i)
Caveat Emptor: I haven't checked above implementation but it should be something like this

You can not use []struct {} for icon, change it to []interface{} instead, or if you want operate on type safe type look at the second solution with cusom unmarshaler
Solution 1
package main
import (
"encoding/json"
"fmt"
"log"
)
type CatalogBank struct {
Advice string `json:"advice"`
BankCode string `json:"bank_code"`
BankName string `json:"bank_name"`
BIC string `json:"bic"`
Credentials []struct {
Label string `json:"label"`
Masked bool `json:"masked"`
} `json:"credentials"`
Icon []interface{} `json:"icon"`
Language []byte `json:"language"`
}
func main() {
data := `
{
"Advice":"abc",
"icon": [
"https://api.figo.me/assets/images/accounts/postbank.png",
{
"48x48": "https://api.figo.me/assets/images/accounts/postbank_48.png",
"60x60": "https://api.figo.me/assets/images/accounts/postbank_60.png",
"72x72": "https://api.figo.me/assets/images/accounts/postbank_72.png",
"84x84": "https://api.figo.me/assets/images/accounts/postbank_84.png",
"96x96": "https://api.figo.me/assets/images/accounts/postbank_96.png",
"120x120": "https://api.figo.me/assets/images/accounts/postbank_120.png",
"144x144": "https://api.figo.me/assets/images/accounts/postbank_144.png",
"192x192": "https://api.figo.me/assets/images/accounts/postbank_192.png",
"256x256": "https://api.figo.me/assets/images/accounts/postbank_256.png"
}
]
}
`
bank := &CatalogBank{}
err := json.Unmarshal([]byte(data), bank)
if err != nil {
log.Fatal(err)
}
for _, icon := range bank.Icon {
fmt.Printf(" %v\n ", icon)
}
}
Solution 2:
package main
import (
"encoding/json"
"fmt"
"log"
)
type Icons struct {
URL string
BySize map[string]string
}
type CatalogBank struct {
Advice string `json:"advice"`
BankCode string `json:"bank_code"`
BankName string `json:"bank_name"`
BIC string `json:"bic"`
Credentials []struct {
Label string `json:"label"`
Masked bool `json:"masked"`
} `json:"credentials"`
Icon *Icons `json:"-,"`
Language []byte `json:"language"`
}
func (p *CatalogBank) Unmarshal(data []byte) error {
type Transient struct {
*CatalogBank
Icon []interface{} `json:"icon"`
}
var transient = &Transient{CatalogBank:p}
err := json.Unmarshal([]byte(data), transient)
if err != nil {
return err
}
p.Icon = &Icons{
BySize: make(map[string]string),
}
if len(transient.Icon) > 0 {
if url, ok := transient.Icon[0].(string); ok {
p.Icon.URL = url
}
if aMap, ok := transient.Icon[1].(map[string]interface{}); ok {
for k, v := range aMap {
p.Icon.BySize[k] = v.(string)
}
}
}
return nil
}
func main() {
data := `
{
"Advice":"abc",
"icon": [
"https://api.figo.me/assets/images/accounts/postbank.png",
{
"48x48": "https://api.figo.me/assets/images/accounts/postbank_48.png",
"60x60": "https://api.figo.me/assets/images/accounts/postbank_60.png",
"72x72": "https://api.figo.me/assets/images/accounts/postbank_72.png",
"84x84": "https://api.figo.me/assets/images/accounts/postbank_84.png",
"96x96": "https://api.figo.me/assets/images/accounts/postbank_96.png",
"120x120": "https://api.figo.me/assets/images/accounts/postbank_120.png",
"144x144": "https://api.figo.me/assets/images/accounts/postbank_144.png",
"192x192": "https://api.figo.me/assets/images/accounts/postbank_192.png",
"256x256": "https://api.figo.me/assets/images/accounts/postbank_256.png"
}
]
}
`
bank := &CatalogBank{}
err := bank.Unmarshal([]byte(data))
if err != nil {
log.Fatal(err)
}
fmt.Printf("advice: %v\n", bank.Advice)
fmt.Printf("icon: %v\n", bank.Icon.URL)
for size, icon := range bank.Icon.BySize {
fmt.Printf("%v => %v\n ",size, icon)
}
}

You can define your icon like this:
package main
import (
"fmt"
"encoding/json"
)
var testIcon = []byte(`{"icon":[
"https://api.figo.me/assets/images/accounts/postbank.png",
{
"48x48":"https://api.figo.me/assets/images/accounts/postbank_48.png"
}]
}`)
func main() {
icon := make(map[string][]interface{})
err := json.Unmarshal(testIcon, &icon)
if err != nil {
panic(err)
}
fmt.Println(icon)
// map[icon:[https://api.figo.me/assets/images/accounts/postbank.png map[48x48:https://api.figo.me/assets/images/accounts/postbank_48.png]]]
}

Related

Unmarshal YAML into complex object which may be either struct or string

Trying to unmarshal YAML into complex object such as map[string]map[interface{}]string.
The problem is that I want to be able to differentiate an interface{} part between string and Source which is a struct.
type Source struct {
ID string `yaml:"id"`
Name string `yaml:"name"`
LogoID string `yaml:"logoId"`
URL string `yaml:"url"`
}
type UNFT struct {
ItemMeta map[string]map[interface{}]string `yaml:"item_meta"`
// could be
// ItemMeta map[string]map[string]string `yaml:"item_meta"`
// or
// ItemMeta map[string]map[Source]string `yaml:"item_meta"`
}
Obviously YAML does not know how to unmarshal into Source struct so I have to implement Unmarshaler interface:
type Unmarshaler interface {
UnmarshalYAML(value *Node) error
}
But I don't quite understand the big picture of unmarshaling process. In general I assume that I have to manually traverse *yaml.Node and call func UnmarshalYAML(value *Node) error on every node.
package main
import (
"fmt"
"gopkg.in/yaml.v3"
)
type Source struct {
ID string `json:"id"`
Name string `json:"name"`
LogoID string `json:"logoId"`
URL string `json:"url"`
}
var data = `
unf:
item_meta:
source:
!struct
? id: "data-watch"
name: "DataWatch"
logoid: "data-watch"
url: "https"
: "product_any('SS')"
public_usage:
"": "source_any('SDF')"
"provider": "source_any('ANO')"`
type UNFT struct {
ItemMeta map[string]map[interface{}]string `yaml:"item_meta"`
}
type MetaConverterConfigT struct {
UNFT UNFT `yaml:"unf"`
}
func main() {
cfg := MetaConverterConfigT{}
err := yaml.Unmarshal([]byte(data), &cfg)
if err != nil {
fmt.Println("%w", err)
}
fmt.Println(cfg)
}
func (s *UNFT) UnmarshalYAML(n *yaml.Node) error {
var cfg map[string]map[interface{}]string
if err := n.Decode(&cfg); err != nil {
fmt.Println("%w", err)
}
return nil
}
Go playground
type MetaKey struct {
String string
Source Source
}
func (k *MetaKey) UnmarshalYAML(n *yaml.Node) error {
if n.Tag == "!!str" {
return n.Decode(&k.String)
}
if n.Tag == "!!map" {
return n.Decode(&k.Source)
}
return fmt.Errorf("unsupported MetaKey type")
}
// ...
type UNFT struct {
ItemMeta map[string]map[MetaKey]string `yaml:"item_meta"`
}
https://go.dev/play/p/Nhtab4l-ANT
If you need the map type to remain as is, i.e. without adding the custom key type, then you can implement the unmarshaler on UNFT as well and just do a re-mapping with any:
type UNFT struct {
ItemMeta map[string]map[any]string `yaml:"item_meta"`
}
func (u *UNFT) UnmarshalYAML(n *yaml.Node) error {
var obj struct {
ItemMeta map[string]map[MetaKey]string `yaml:"item_meta"`
}
if err := n.Decode(&obj); err != nil {
return err
}
u.ItemMeta = make(map[string]map[any]string, len(obj.ItemMeta))
for k, v := range obj.ItemMeta {
m := make(map[any]string, len(v))
for k, v := range v {
if k.Source != (Source{}) {
m[k.Source] = v
} else {
m[k.String] = v
}
}
u.ItemMeta[k] = m
}
return nil
}
https://go.dev/play/p/uwboGKf3qnD

How to unmarshal JSOn with an array of different types

I have JSON like this that I need to parse into a golang type:
{
name: "something"
rules: [
{
"itemTypeBasedConditions": [["containsAny", ["first_match", "second_match"]]],
"validity": "INVALID"
}]
}
The problem is that each array of the array in itemTypeBasedConditions contains a mix of strings (always first element) and another array (second element), and I am not sure how to parse all of that into an object that I could then manipulate.
I got to:
type RulesFile struct {
Name string
Rules []RulesItem
}
type RulesItem struct {
itemTypeBasedConditions [][]interface{}
validity bool
}
And then I guess I have to convert elements one by one from interface{} to either string (containsAny) or an array of strings ("first_match", "second_match")
Is there a better way of approaching this JSON parsing?
I would do something like this, you can probably alter this to your needs.
package main
import (
"encoding/json"
"fmt"
"os"
"reflect"
)
type RulesFile struct {
Name string `json:"name"`
Rules []RulesItem `json:"rules"`
}
type RulesItem struct {
ItemTypeBasedConditions [][]Condition `json:"itemTypeBasedConditions"`
Validity bool `json:"validity"`
}
type Condition struct {
Value *string
Array *[]string
}
func (c Condition) String() string {
if c.Value != nil {
return *c.Value
}
return fmt.Sprintf("%v", *c.Array)
}
func (c *Condition) UnmarshalJSON(data []byte) error {
var y interface{}
err := json.Unmarshal(data, &y)
if err != nil {
return err
}
switch reflect.TypeOf(y).String() {
case "string":
val := fmt.Sprintf("%v", y)
c.Value = &val
return nil
case "[]interface {}":
temp := y.([]interface{})
a := make([]string, len(temp))
for i, v := range temp {
a[i] = fmt.Sprint(v)
}
c.Array = &a
return nil
}
return fmt.Errorf("cannot unmarshall into string or []string: %v", y)
}
var input string = `
{
"name": "something",
"rules": [
{
"itemTypeBasedConditions": [["containsAny",["first_match", "second_match"]]],
"validity": false
}
]
}`
func main() {
var ruleFile RulesFile
err := json.Unmarshal([]byte(input), &ruleFile)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Printf("%+v\n", ruleFile)
}
You can implement the json.Unmarshaler interface. Have that implementation first unmarshal the json into a slice of json.RawMessage, then, once you've done that, you can unmarshal the individual elements to their corresponding types.
type Cond struct {
Name string
Args []string
}
func (c *Cond) UnmarshalJSON(data []byte) error {
// unmarshal into a slice of raw json
var raw []json.RawMessage
if err := json.Unmarshal(data, &raw); err != nil {
return err
} else if len(raw) != 2 {
return errors.New("unsupported number of elements in condition")
}
// unmarshal the first raw json element into a string
if err := json.Unmarshal(raw[0], &c.Name); err != nil {
return err
}
// unmarshal the second raw json element into a slice of string
return json.Unmarshal(raw[1], &c.Args)
}
https://go.dev/play/p/-tbr73TvX0d

Iterating list json object in golang

I have this piece of code to read a JSON object. I need to easily iterate over all the elements in the 'outputs'/data/concepts key.
Is there a better way to do it?
Also, how can I access the attributes of value:
value.app_id, value.id..etc
Code:
package main
import (
"encoding/json"
"fmt"
)
var jsonBytes = []byte(`
{"outputs": [{
"data": {"concepts":
[{"app_id": "main",
"id": "ai_GTvMbVGh",
"name": "ancient",
"value": 0.99875855}]
}}
],
"status": {"code": 10000, "description": "Ok"}}`)
func main() {
var output map[string]interface{}
err := json.Unmarshal([]byte(jsonBytes), &output)
if err != nil {
print(err)
}
for _, value := range output["outputs"].([]interface{}) {
//fmt.Println(value.(map[string]interface{})["data"].(map[string]interface{})["concepts"]).([]interface{})
//fmt.Println(value.(map[string]interface{})["data"].(map[string]interface{})["concepts"])
for _, value := range value.(map[string]interface{})["data"].(map[string]interface{})["concepts"].([]interface{}){
fmt.Println(value)
}
}
//fmt.Printf("%+v\n", output)
}
the best way will be to Unmarshal the JSON into an struct and iterate over the values,
func main() {
var output StructName
err := json.Unmarshal([]byte(jsonBytes), &output)
if err != nil {
print(err)
}
for _, value := range output.Outputs {
for _, val := range value.Data.Concepts {
fmt.Printf("AppId:%s\nID:%s\nname:%s\nvalue:%f", val.AppID, val.ID, val.Name, val.Value)
}
}
}
type StructName struct {
Outputs []struct {
Data struct {
Concepts []struct {
AppID string `json:"app_id"`
ID string `json:"id"`
Name string `json:"name"`
Value float64 `json:"value"`
} `json:"concepts"`
} `json:"data"`
} `json:"outputs"`
Status struct {
Code int `json:"code"`
Description string `json:"description"`
} `json:"status"`
}

Unmarshal map[string]DynamoDBAttributeValue into a struct

I'm trying to set-up an AWS-lambda using aws-sdk-go that is triggered whenever a new user is added to a certain dynamodb table.
Everything is working just fine but I can't find a way to unmarshal a map map[string]DynamoDBAttributeValue like:
{
"name": {
"S" : "John"
},
"residence_address": {
"M": {
"address": {
"S": "some place"
}
}
}
}
To a given struct, for instance, a User struct. Here is shown an example of unsmarhaling a map[string]*dynamodb.AttributeValue into a given interface, but I can't find a way to do the same thing with map[string]DynamoDBAttributeValue even though these types seem to fit the same purposes.
map[string]DynamoDBAttributeValue is returned by a events.DynamoDBEvents from package github.com/aws/aws-lambda-go/events. This is my code:
package handler
import (
"context"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
"github.com/aws/aws-sdk-go/service/dynamodb"
)
func HandleDynamoDBRequest(ctx context.Context, e events.DynamoDBEvent) {
for _, record := range e.Records {
if record.EventName == "INSERT" {
// User Struct
var dynamoUser model.DynamoDBUser
// Of course this can't be done for incompatible types
_ := dynamodbattribute.UnmarshalMap(record.Change.NewImage, &dynamoUser)
}
}
}
Of course, I can marshal record.Change.NewImage to JSON and unmarshal it back to a given struct, but then, I would have to manually initialize dynamoUser attributes starting from the latter ones.
Or I could even write a function that parses map[string]DynamoDBAttributeValue to map[string]*dynamodb.AttributeValue like:
func getAttributeValueMapFromDynamoDBStreamRecord(e events.DynamoDBStreamRecord) map[string]*dynamodb.AttributeValue {
image := e.NewImage
m := make(map[string]*dynamodb.AttributeValue)
for k, v := range image {
if v.DataType() == events.DataTypeString {
s := v.String()
m[k] = &dynamodb.AttributeValue{
S : &s,
}
}
if v.DataType() == events.DataTypeBoolean {
b := v.Boolean()
m[k] = &dynamodb.AttributeValue{
BOOL : &b,
}
}
// . . .
if v.DataType() == events.DataTypeMap {
// ?
}
}
return m
}
And then simply use dynamodbattribute.UnmarshalMap, but on events.DataTypeMap it would be quite a tricky process.
Is there a way through which I can unmarshal a DynamoDB record coming from a events.DynamoDBEvent into a struct with a similar method shown for map[string]*dynamodb.AttributeValue?
I tried the function you provided, and I met some problems with events.DataTypeList, so I managed to write the following function that does the trick:
// UnmarshalStreamImage converts events.DynamoDBAttributeValue to struct
func UnmarshalStreamImage(attribute map[string]events.DynamoDBAttributeValue, out interface{}) error {
dbAttrMap := make(map[string]*dynamodb.AttributeValue)
for k, v := range attribute {
var dbAttr dynamodb.AttributeValue
bytes, marshalErr := v.MarshalJSON(); if marshalErr != nil {
return marshalErr
}
json.Unmarshal(bytes, &dbAttr)
dbAttrMap[k] = &dbAttr
}
return dynamodbattribute.UnmarshalMap(dbAttrMap, out)
}
I was frustrated that the type of NewImage from the record wasn't map[string]*dynamodb.AttributeValue so I could use the dynamodbattribute package.
The JSON representation of events.DynamoDBAttributeValue seems to be the same as the JSON represenation of dynamodb.AttributeValue.
So I tried creating my own DynamoDBEvent type and changed the type of OldImage and NewImage, so it would be marshalled into map[string]*dynamodb.AttributeValue instead of map[string]events.DynamoDBAttributeValue
It is a little bit ugly but it works for me.
package main
import (
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
"fmt"
)
func main() {
lambda.Start(lambdaHandler)
}
// changed type of event from: events.DynamoDBEvent to DynamoDBEvent (see below)
func lambdaHandler(event DynamoDBEvent) error {
for _, record := range event.Records {
change := record.Change
newImage := change.NewImage // now of type: map[string]*dynamodb.AttributeValue
var item IdOnly
err := dynamodbattribute.UnmarshalMap(newImage, &item)
if err != nil {
return err
}
fmt.Println(item.Id)
}
return nil
}
type IdOnly struct {
Id string `json:"id"`
}
type DynamoDBEvent struct {
Records []DynamoDBEventRecord `json:"Records"`
}
type DynamoDBEventRecord struct {
AWSRegion string `json:"awsRegion"`
Change DynamoDBStreamRecord `json:"dynamodb"`
EventID string `json:"eventID"`
EventName string `json:"eventName"`
EventSource string `json:"eventSource"`
EventVersion string `json:"eventVersion"`
EventSourceArn string `json:"eventSourceARN"`
UserIdentity *events.DynamoDBUserIdentity `json:"userIdentity,omitempty"`
}
type DynamoDBStreamRecord struct {
ApproximateCreationDateTime events.SecondsEpochTime `json:"ApproximateCreationDateTime,omitempty"`
// changed to map[string]*dynamodb.AttributeValue
Keys map[string]*dynamodb.AttributeValue `json:"Keys,omitempty"`
// changed to map[string]*dynamodb.AttributeValue
NewImage map[string]*dynamodb.AttributeValue `json:"NewImage,omitempty"`
// changed to map[string]*dynamodb.AttributeValue
OldImage map[string]*dynamodb.AttributeValue `json:"OldImage,omitempty"`
SequenceNumber string `json:"SequenceNumber"`
SizeBytes int64 `json:"SizeBytes"`
StreamViewType string `json:"StreamViewType"`
}
I have found the same problem and the solution is to perform a simple conversion of types. This is possible because in the end the type received by lambda events events.DynamoDBAttributeValue and the type used by the SDK V2 of AWS DynamoDB types.AttributeValue are the same. Next I show you the conversion code.
package aws_lambda
import (
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue"
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)
func UnmarshalDynamoEventsMap(
record map[string]events.DynamoDBAttributeValue, out interface{}) error {
asTypesMap := DynamoDbEventsMapToTypesMap(record)
err := attributevalue.UnmarshalMap(asTypesMap, out)
if err != nil {
return err
}
return nil
}
func DynamoDbEventsMapToTypesMap(
record map[string]events.DynamoDBAttributeValue) map[string]types.AttributeValue {
resultMap := make(map[string]types.AttributeValue)
for key, rec := range record {
resultMap[key] = DynamoDbEventsToTypes(rec)
}
return resultMap
}
// DynamoDbEventsToTypes relates the dynamo event received by AWS Lambda with the data type that is
// used in the Amazon SDK V2 to deal with DynamoDB data.
// This function is necessary because Amazon does not provide any kind of solution to make this
// relationship between types of data.
func DynamoDbEventsToTypes(record events.DynamoDBAttributeValue) types.AttributeValue {
var val types.AttributeValue
switch record.DataType() {
case events.DataTypeBinary:
val = &types.AttributeValueMemberB{
Value: record.Binary(),
}
case events.DataTypeBinarySet:
val = &types.AttributeValueMemberBS{
Value: record.BinarySet(),
}
case events.DataTypeBoolean:
val = &types.AttributeValueMemberBOOL{
Value: record.Boolean(),
}
case events.DataTypeList:
var items []types.AttributeValue
for _, value := range record.List() {
items = append(items, DynamoDbEventsToTypes(value))
}
val = &types.AttributeValueMemberL{
Value: items,
}
case events.DataTypeMap:
items := make(map[string]types.AttributeValue)
for k, v := range record.Map() {
items[k] = DynamoDbEventsToTypes(v)
}
val = &types.AttributeValueMemberM{
Value: items,
}
case events.DataTypeNull:
val = nil
case events.DataTypeNumber:
val = &types.AttributeValueMemberN{
Value: record.Number(),
}
case events.DataTypeNumberSet:
val = &types.AttributeValueMemberNS{
Value: record.NumberSet(),
}
case events.DataTypeString:
val = &types.AttributeValueMemberS{
Value: record.String(),
}
case events.DataTypeStringSet:
val = &types.AttributeValueMemberSS{
Value: record.StringSet(),
}
}
return val
}
There is a package that allows conversion from events.DynamoDBAttributeValue to dynamodb.AttributeValue
https://pkg.go.dev/github.com/aereal/go-dynamodb-attribute-conversions/v2
From there one can unmarshal AttributeValue into struct
func Unmarshal(attribute map[string]events.DynamoDBAttributeValue, out interface{}) error {
av := ddbconversions.AttributeValueMapFrom(attribute)
return attributevalue.UnmarshalMap(av, out)
}

How to marshal struct when some members are protected/inner/hidden

How do ensure the fields in this LookupCode struct are included when marshalling?
package main
import (
"encoding/json"
"fmt"
)
type LookupCode struct {
code string `json:"code"`
name string `json:"name"`
}
func (l *LookupCode) GetCode() string {
return l.code
}
func main() {
c := &LookupCode{
code: "A",
name: "Apple",
}
b, _ := json.MarshalIndent(c, "", "\t")
fmt.Println(string(b))
}
http://play.golang.org/p/my52DAn0-Z
You can by implementing the json.Marshaller interface:
Full Example: http://play.golang.org/p/8mIcPwX92P
// Implement json.Unmarshaller
func (l *LookupCode) UnmarshalJSON(b []byte) error {
var tmp struct {
Code string `json:"code"`
Name string `json:"name"`
}
err := json.Unmarshal(b, &tmp)
if err != nil {
return err
}
l.code = tmp.Code
l.name = tmp.Name
return nil
}
func (l *LookupCode) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Code string `json:"code"`
Name string `json:"name"`
}{
Code: l.code,
Name: l.name,
})
}
encode/json cannot marshal unexported fields. Change your code to:
type LookupCode struct {
Code string `json:"code"`
Name string `json:"name"`
}
and do the same wherever you use code or name.
Playground: http://play.golang.org/p/rak0nVCNGI
Edit
The limitation is due to the reflection used when marshalling the struct. If you need to keep your values unexported, you must implement the json.Marshaller interface and do the encoding manually.
if the struct has only string-type fields,you can try this hack way.
package main
import (
"fmt"
"reflect"
"github.com/bitly/go-simplejson"
)
type A struct {
name string `json:"name"`
code string `json:"code"`
}
func marshal(a A) ([]byte, error) {
j := simplejson.New()
va := reflect.ValueOf(&a)
vt := va.Elem()
types := reflect.TypeOf(a)
for i := 0; i < vt.NumField(); i++ {
j.Set(types.Field(i).Tag.Get("json"), fmt.Sprintf("%v", reflect.Indirect(va).Field(i)))
}
return j.MarshalJSON()
}
func main() {
a := A{name: "jessonchan", code: "abc"}
b, _ := marshal(a)
fmt.Println(string(b))
}

Resources