0

At the input, the program receives a JSON.

I need to return the same JSON, but with camelCase keys.

Is there any effective way to convert all snake_case keys in JSON to camelCase keys in Go?

Snippet Code:

package main

import (
    "encoding/json"
    "fmt"
    "reflect"
    "regexp"
    "strings"
)

type CategoryList struct {
    Id   *string `json:"id"`
    Name *string `json:"name"`
}

type Data struct {
    AccessToken  *string         `json:"access_token"`
    Category     *string         `json:"category"`
    CategoryList []*CategoryList `json:"category_list"`
    Name         *string         `json:"name"`
    Id           *string         `json:"id"`
    Tasks        []*string       `json:"tasks"`
}

type Paging struct {
    Cursors *Cursors `json:"cursors"`
}

type Cursors struct {
    Before *string `json:"before"`
    After  *string `json:"after"`
}

type Categories struct {
    Data   []*Data `json:"data"`
    Paging *Paging `json:"paging"`
}

func main() {
    jsonString := `{"data":[{"access_token":"ABCDEFGHIJKLMNOPQRSTUVWXYZ","category":"First Category","category_list":[{"id":"2301","name":"Finance App"}]},{"access_token":"ZYXWVUTSRQPONMLKJIHGFEDCBA","category":"Second Category","category_list":[{"id":"2302","name":"Medical App"}]}],"paging":{"cursors":{"before":"MTA0MjcxMzIxNjYxNTkz","after":"MTA3MTA5NDcxNDc5MTk3"}}}`

    var categories map[string]interface{}
    if err := json.Unmarshal([]byte(jsonString), &categories); err != nil {
        panic(err)
    }

    modified := fromSnakeCaseToCamelCase(reflect.ValueOf(categories))

    result, err := json.Marshal(modified.Interface().(map[string]interface{}))
    if err != nil {
        panic(err)
    }
    fmt.Println(string(result))
}

func fromSnakeCaseToCamelCase(value reflect.Value) reflect.Value {
    switch value.Kind() {
        case reflect.Array, reflect.Slice:
            for i := 0; i < value.Len(); i++ {
                nestedValue := value.Index(i)

                fromSnakeCaseToCamelCase(nestedValue)
            }
        case reflect.Map:
            for _, keyName := range value.MapKeys() {
                nestedValue := value.MapIndex(keyName)

                newKeyName := reflect.ValueOf(convertToCamelCase(keyName.Interface().(string)))
                value.SetMapIndex(newKeyName, nestedValue)

                oldKeyName := reflect.ValueOf(keyName.Interface().(string))
                value.SetMapIndex(oldKeyName, reflect.Value{})

                fromSnakeCaseToCamelCase(nestedValue)
            }
        default:
            // pass
    }
    return value
}

var link = regexp.MustCompile("(^[A-Za-z]|_[A-Za-z])")

func convertToCamelCase(str string) string {
    return link.ReplaceAllStringFunc(str, func(s string) string {
        r := strings.NewReplacer("_", "")
        return strings.ToUpper(r.Replace(s))
    })
}
Nurzhan Nogerbek
  • 4,806
  • 16
  • 87
  • 193
  • Does this answer your question? [converting all snake\_case keys in a json to camelCase keys in golang](https://stackoverflow.com/questions/46979031/converting-all-snake-case-keys-in-a-json-to-camelcase-keys-in-golang) – Peter Mar 31 '21 at 19:12
  • Hello, @Peter! I read this post on StackOverflow. There are two options in your answer under this post. The first option in my opinion increases the code base in my case since there are a lot of structs that need to be duplicated. As you can see, I have a lot of nesting. The second option that you suggested will convert everything to UPPERCASE. – Nurzhan Nogerbek Mar 31 '21 at 19:24
  • The type conversion doesn't work if you have nested struct types. You can easily replace the fixKeys function in the linked answer with one that does the conversion to camel case (as mentioned in the answer). – Peter Mar 31 '21 at 19:58

2 Answers2

1

You can unmarshal the JSON into an interface{}, which will produce a map[string]interface{} for each JSON object. Recursively traverse the structure and replace keys as necessary, then marshal it back to JSON again.

Thomas
  • 174,939
  • 50
  • 355
  • 478
  • Thanks for the answer! Your idea sounds great. I'm trying to implement it now. Could you please check my post again? I added a new code. I managed to achieve a recursion, but it don't work as I expected. Could you please direct me in the right direction? – Nurzhan Nogerbek Apr 01 '21 at 10:06
1

You could use a custom json marshalling function for your struct which could use a regex to replace snake case to camel case for the keys.

Otherwise I think a simple way would be to define two structs, one with camel and one with snake. You can unmarshal into one, concert between the two and then re-marshal and send as a json response.

Christian
  • 1,676
  • 13
  • 19
  • Thanks for the reply. I like your first idea that you suggested. Could you please provide a small example in the context of my problem? What should the `regex` rules look like for replacing the `snake_case` with the `camelCase` in the `MarshalJSON` function? The second idea in my opinion increases the code base in my case since there are a lot of structs that need to be duplicated. As you can see, I have a lot of nesting. – Nurzhan Nogerbek Mar 31 '21 at 19:48