go-kratos / kratos

Your ultimate Go microservices framework for the cloud-native era.
https://go-kratos.dev
MIT License
23.13k stars 3.99k forks source link

[Question]Why kratos load yaml as json and unmarshal to struct #3153

Closed FocusHuang closed 4 months ago

FocusHuang commented 8 months ago

we can use fill time.duration as 1s/1h in yaml/yml, like timeout: 1s, but the feature in kratos will encode it to json, and then, the time.duration field will be an error field to unmarshal to go struct

dosubot[bot] commented 8 months ago

🤖

Hello @FocusHuang! 👋

I'm Dosu, a friendly bot here to assist you while we wait for a human maintainer. I'm equipped to help with bug solving, answering questions, and guiding you on your path to becoming a contributor. Don't hesitate to ask anything, I'm here to help!

The issue you're encountering seems to be related to how Go's encoding/json package unmarshals data into a time.Duration field. When a time.Duration field is unmarshaled, the encoding/json package expects the duration to be a JSON number representing the duration in nanoseconds. This is why you're seeing an error when trying to unmarshal a string like "1s" or "1h" into a time.Duration field.

However, when you're using YAML, durations can be represented as strings like "1s" or "1h", which is more human-readable than a large number of nanoseconds. This discrepancy between the JSON and YAML representations of durations is likely what's causing the issue.

One possible solution to this issue is to use a custom type for durations in your Go structs, and implement the json.Unmarshaler and json.Marshaler interfaces for this type. This would allow you to control how durations are marshaled and unmarshaled, and you could choose to use the human-readable string representation for both JSON and YAML.

Here's an example of how you could implement this:

type Duration struct {
    time.Duration
}

func (d Duration) MarshalJSON() ([]byte, error) {
    return json.Marshal(d.String())
}

func (d *Duration) UnmarshalJSON(b []byte) error {
    var s string
    if err := json.Unmarshal(b, &s); err != nil {
        return err
    }
    duration, err := time.ParseDuration(s)
    if err != nil {
        return err
    }
    d.Duration = duration
    return nil
}

With this custom Duration type, you can now use durations in your Go structs like this:

type MyStruct struct {
    Timeout Duration `json:"timeout"`
}

And you can use human-readable durations in your YAML files:

timeout: 1s

When this YAML file is loaded and unmarshaled into MyStruct, the Timeout field will correctly be a time.Duration of 1 second.

I hope this helps! If you have any further questions, please let me know.

Sources


This response is meant to be useful and save you time. It is not meant to be a precise solution, but rather a starting point for your own research.

Help me be more useful! Please leave a 👍 if this is helpful and 👎 if it is irrelevant.

If you want to continue the conversation, start your reply with @dosu-bot.