#Unmarshal Custom Type Issue

27 messages · Page 1 of 1 (latest)

unkempt slate
#

I am trying to unmarshal the custom type Percent from a string. When I run the code below, I get the following error: Error: invalid character '%' after top-level value. I assumed that I would have at least hit the UnmarshalJSON method, but it does not.

type Percent float32

func (p *Percent) UnmarshalJSON(data []byte) error {
    fmt.Println("Unmarshalling Percent")
    s := strings.TrimRight(strings.TrimRight(string(data), "%"), " ")
    v, err := strconv.ParseFloat(s, 32)
    if err != nil {
        return err
    }

    *p = Percent(v)

    return nil
}

func main() {
    s := "50%"
    var p Percent
    err := json.Unmarshal([]byte(s), &p)

    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Parsed Percent:", p)
    }
}
inland pulsar
#
type Percent float32

func (p *Percent) UnmarshalJSON(data []byte) error {
    fmt.Println("Unmarshalling Percent")
    s := strings.Trim(string(data), `" %`)
    fmt.Println(s)
    v, err := strconv.ParseFloat(s, 32)
    if err != nil {
        return err
    }

    *p = Percent(v)

    return nil
}

func main() {
    s := []byte(`{"p": " 50% " }`)
    var p struct {
        Prc Percent `json:"p"`
    }
    err := json.Unmarshal(s, &p)

    //...
}
Unmarshalling Percent
50
Parsed Percent: {50}

There are several bugs, but overall you almost got it

  1. data []byte gets passed with " quotes, so for " 50% " value you're getting exactly " 50% ", not 50%.
  2. I could be wrong on this, but I believe minimal JSON is a single k-v pair, so {"k":"v"} format is required
#

also json tags

#

you could try minimizing/refactoring what I've done, but that's a start I believe

unkempt slate
#

Ok, I'll give this a shot! I think you are right on the need to have a k-v pair

fossil kelp
unkempt slate
#

I also thought "50%" would give me the valid json value of "50%", but that didnt work

inland pulsar
#

well "50%" is a valid string xd

unkempt slate
#

O wait! Nvm I might be close, one second...

#

if i do "50%" I think the issue is with how I am trimming our the %.

inland pulsar
#

You could check mine

#

I changed it because you were doing some extra work

unkempt slate
#

I will check that one also!

fossil kelp
inland pulsar
#

triple backticks? (not a one-liner tho)

s:=`"50%"`
fossil kelp
#

if I were smart I would have thought of that sunturtle

unkempt slate
#

@fossil kelp I will get back to yours shortly, I am still trying to reason about mine, but I think I will ultimately need to make a struct like you suggested.

fossil kelp
#

you don't need a struct, you just need a valid json string
s := "50%" is not a valid json string as json strings need quotes

#

remember you are expressing json in terms of go syntax

unkempt slate
#

Ahhh, ok I think I see what is going on now!

#

There are a couple things at play here, I have working code! And I want to write out my logic to whats happening

#
type Percent float32

func (p *Percent) UnmarshalJSON(data []byte) error {
    fmt.Println("Unmarshalling Percent", string(data))
    s := strings.Trim(string(data), "% \"")

    fmt.Println("About to parse", s)
    v, err := strconv.ParseFloat(s, 32)
    if err != nil {
        return err
    }

    *p = Percent(v)

    return nil
}

func main() {
    s := `"50%"`
    var p Percent
    err := json.Unmarshal([]byte(s), &p)

    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Parsed Percent:", p)
    }
}
#

Given the code above, we start with the JSON string of "50%" which in go syntax we must write as "50%". Unmarshal correctly attempts to send that to the Percent method. So inside the method, we can see we are working with string "50%", however I need to convert that JSON string into a JSON number. So I need to remove both the % and " characters and just be left iwth 50 which is a valid JSON number.

#

50 then is correctly parsed into a float as expected and returned

inland pulsar
#

not super critical but you could use backticks in your Trim to avoid escaping "

unkempt slate
#

Good call, that looks much cleaner

#

Phew, what a ride. lol. So TLDR, you have to make the percent string inside the JSON string into just a number inside the JSON string to then parse to a float