停止json.Marshal()从浮点数剥离尾随零

时间:2018-09-21 15:16:59

标签: json go marshalling

我遇到以下问题: 我的golang程序将一些信息转换为JSON。 例如,它导致以下json:

{
   "value":40,
   "unit":"some_string"
}

问题是值的“输入”为40.0,编组剥离了结尾的零。如果读取JSON的EPL能够将浮点数读取为40而没有.0

,那将是没有问题的

因此JSON输出应类似于:

{
   "value":40.0,
   "unit":"some_string"
}

是否可以“阻止” json.Marshal()删除零?

编辑:值必须是浮点数

5 个答案:

答案 0 :(得分:3)

默认情况下,如果浮点数的值为整数,则不带小数点和小数。表示较短,表示相同的数字。

如果要控制数字在JSON表示中的显示方式,请使用json.Number类型。

示例:

type Pt struct {
    Value json.Number
    Unit  string
}

func main() {
    data, err := json.Marshal(Pt{json.Number("40.0"), "some_string"})
    fmt.Println(string(data), err)
}

输出(在Go Playground上尝试):

{"Value":40.0,"Unit":"some_string"} <nil>

如果您将数字作为float64值,则可以将其转换为json.Number,如下所示:

func toNumber(f float64) json.Number {
    var s string
    if f == float64(int64(f)) {
        s = fmt.Sprintf("%.1f", f) // 1 decimal if integer
    } else {
        s = fmt.Sprint(f)
    }
    return json.Number(s)
}

测试:

f := 40.0
data, err := json.Marshal(Pt{toNumber(f), "some_string"})
fmt.Println(string(data), err)

f = 40.123
data, err = json.Marshal(Pt{toNumber(f), "some_string"})
fmt.Println(string(data), err)

输出(在Go Playground上尝试):

{"Value":40.0,"Unit":"some_string"} <nil>
{"Value":40.123,"Unit":"some_string"} <nil>

另一个方向,如果您想要float64的{​​{1}}值,只需调用其Number.Float64()方法。

答案 1 :(得分:1)

@icza提供了一个很好的答案,但只是提供了另一个选择,您可以定义自己的浮点类型并为其定义序列化。像这样

type KeepZero float64

func (f KeepZero) MarshalJSON() ([]byte, error) {
    if float64(f) == float64(int(f)) {
        return []byte(strconv.FormatFloat(float64(f), 'f', 1, 32)), nil
    }
    return []byte(strconv.FormatFloat(float64(f), 'f', -1, 32)), nil
}

type Pt struct {
    Value KeepZero
    Unit  string
}

func main() {
    data, err := json.Marshal(Pt{40.0, "some_string"})
    fmt.Println(string(data), err)
}

结果为{"Value":40.0,"Unit":"some_string"} <nil>Check it out in playground.

答案 2 :(得分:0)

将值另存为字符串,并在需要时将其强制返回。

答案 3 :(得分:0)

我遇到类似的问题,我想将浮点值为f {x map[string]interface{}的{​​{1}}编组为JSON,1.0。我通过为自定义浮动类型添加自定义Marshal函数,然后将地图中的浮动替换为自定义类型来解决了此问题:

1.0

然后替换所有float64节点:

type customFloat float64

func (f customFloat) MarshalJSON() ([]byte, error) {
    if float64(f) == math.Trunc(float64(f)) {
        return []byte(fmt.Sprintf("%.1f", f)), nil
    }
    return json.Marshal(float64(f))
}

func replaceFloat(value map[string]interface{}) {
    for k, v := range value {
        switch val := v.(type) {
        case map[string]interface{}:
            replaceFloat(val)
        case float64:
            value[k] = customFloat(val)
        }
    }
}

这将打印出replaceFloat(myValue) bytes, err := json.Marshal(myValue) 之类的浮点数

答案 4 :(得分:0)

import datetime
# I started by creating lists for the months, their days, and the days of the week.
month_list = ['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december']
days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
days_of_week = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']

def calendar(month, year):
    '''
    (int, int) -> None

    Prints a calendar for
    given month and year.

    CALLS: is_leap(year)

    CALLED BY: main()
    '''
    # I first wanted to check if the year is a leap year, and if so, modify the days in feb
    if is_leap(year):
        days_in_month[1] = 29
    # Now i set variables to values within my lists based of input data for year and month
    month_corrected = month_list[month-1]
    num_days = days_in_month[month-1]
    # Now I figure out what my start date is for the given month of the year
    day_one = datetime.date(year, month, 1)
    start_day = day_one.isoweekday()
    # Begin by printing the month and year
    print(month_corrected[:3], year)
    # Print the days of the week for my calendar
    print(' '.join(['{0:<2}'.format(w) for w in days_of_week]))
    # Print a blank space on the days before my start day variable
    print('{0:>3}'.format('')*start_day, end='')
    # if my start day begins on sunday, start a new line and set my start day to zero
    if start_day >= 7:
        print()
        start_day = 0
    # Begin looping through the number of days in the month starting with my start day
    for day in range(1, num_days+1):
        # Print each day
        print('{0:>2}'.format(day), end=' ')
        # up my start day variable each time to act as a counter
        start_day += 1
        if start_day >= 7:
            # If my start day variable is on Sunday, start new line
            print()
            # Reset my day counter
            start_day = 0
    print()

def is_leap(year):
    '''
    (int) -> bool

    Checks if year is a leap year
    credit to Steven Summers

    CALLS:

    CALLED BY: calendar()

    >>> is_leap(2000)
    True
    >>> is_leap(1900)
    False
    '''
    if year % 4 == 0:
        if year % 100 == 0:
            if year % 400 == 0:
                return True
            else:
                return False
        else:
            return True
    else:
        return False

def main():
    '''
    () -> None

    Drives calendar program

    CALLS: calendar(month, year)

    CALLED BY:
    '''
    make_calendars = True
    while make_calendars:
        # create input loops for year and month to run calendar function
        good_year = False
        while good_year == False:
            input_year = input('Enter year (blank to exit): ')
            # some code to ensure input is within datetime parameters
            if input_year == '':
                make_calendars = False
                return None
            else:
                year = int(input_year)
                if 1 > year > 9999:
                    print("Must enter an integer between 1 and 9999")
                else:
                    good_year = True        
        good_month = False
        while good_month == False:
            # a blank entry will print the entire year
            input_month = input('Enter month (blank to print entire year: ')
            # some code to ensure input is within datetime parameters
            if input_month == '':
                for i in range(1,13):
                    calendar(i, year)
                main()
            else:
                if input_month[0] == '0':
                    input_month = input_month[1:]                
                month = int(input_month)
                if 1 > month > 12:
                    print("Must enter an integer between 1 and 12")
                else:
                    good_month = True 
        if good_year == True and good_month == True:
            calendar(month, year)

if __name__ == '__main__':
    main()

这样使用

  type MyFloat float64
  func (mf MyFloat) MarshalJSON() ([]byte, error) {                                                          
      return []byte(fmt.Sprintf("%.1f", float64(mf))), nil                                                     
  }

以您需要的精度替换 type PricePoint struct { Price MyFloat `json:"price"` From time.Time `json:"valid_from"` To time.Time `json:"valid_to"` } 中的1