go-json-experiment / json

Experimental implementation of a proposed v2 encoding/json package
BSD 3-Clause "New" or "Revised" License
341 stars 11 forks source link

json: cannot unmarshal JSON number into Go value of type time.Duration #4

Closed crawshaw closed 10 months ago

crawshaw commented 2 years ago

I attempted to drop this package in as a replacement for encoding/json. It is incapable of decoding a value encoded by encoding/json. Is this expected?

package main

import (
    "log"
    "time"

    jsonexp "github.com/go-json-experiment/json"
)

func main() {
    var v struct {
        D time.Duration
    }
    src := []byte(`{"Name":"","D":155520000000000}`)
    if err := jsonexp.Unmarshal(src, &v); err != nil {
        log.Fatal(err)
    }
}

Produces:

$ go run junk.go 
2022/05/16 06:02:18 json: cannot unmarshal JSON number into Go value of type time.Duration
dsnet commented 2 years ago

The time.Duration type is serialized using the string representation (e.g., "123m456.789s").

The legacy behavior can be set using a format flag:

var v struct {
    D time.Duration `json:",format:nanos"`
}

The change in default behavior is to accommodate the fact that most users do not want time.Duration serialized a decimal representation of nanoseconds.

In the Tailscale code base, there is even this type:

// TextDuration wraps time.TextDuration to provide better text marshalling support.
// It fills in the gap from https://github.com/golang/go/issues/10275.
type TextDuration time.Duration

func (d TextDuration) MarshalText() ([]byte, error)
func (d *TextDuration) UnmarshalText(b []byte) error
dsnet commented 2 years ago

Some of the notable changes between "encoding/json" and this package can be found here: https://github.com/go-json-experiment/json/blob/master/diff_test.go

mvdan commented 2 years ago

Is there anything else to do here? :)

deefdragon commented 1 year ago

Would it be possible to add a way to override how certain types are marshaled? Duration is a commonly shown example of a miss in the original encoding (and I think IP and URL both fall prey to bad encodings too off the top of my head), but I feel there will be others with time.

Having a way to override marshaling may be benifical in the long run to both the standard library AND users who want custom marshaling of types in 3rd party libraries, but I dont know the complexity of adding that.

mvdan commented 1 year ago

@deefdragon you mean https://pkg.go.dev/github.com/go-json-experiment/json#Marshalers?

deefdragon commented 1 year ago

Yep. Apologies. that was not listed in the Behavior changes section on the readme, but I should have done more due-diligence as to checking for it in the docs.

dsnet commented 10 months ago

The jsonv1.FormatTimeDurationAsNanosecond call option provides a for jsonv2.Marshal to be configured to use the legacy behavior.