这个问题分为两部分:查找给定JSON名称的字段和按字段排序。

让我们从第二部分的代码开始,按字段名对切片进行排序。这里有一个函数,可以对任意结构类型的结构片段或指向结构的指针片段进行排序。有关详细信息,请参阅评论。

// sortByField sorts slice by the named field.
// The slice argument must be a slice of struct or
// a slice of pointer to struct.
func sortByField(slice interface{}, fieldName string) error {
    v := reflect.ValueOf(slice)
    if v.Kind() != reflect.Slice {
        return fmt.Errorf("got %T, expected slice", slice)
    }

    // Get slice element type.

    t := v.Type().Elem()

    // Handle pointer to struct.

    indirect := t.Kind() == reflect.Ptr
    if indirect {
        t = t.Elem()
    }

    if t.Kind() != reflect.Struct {
        return fmt.Errorf("got %T, expected slice of struct or pointer to struct", slice)
    }

    // Find the field.

    sf, ok := t.FieldByName(fieldName)
    if !ok {
        return fmt.Errorf("field name %s not found", fieldName)
    }

    // Create a less function based on the field's kind.

    var less func(a, b reflect.Value) bool
    switch sf.Type.Kind() {
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
        less = func(a, b reflect.Value) bool { return a.Int() < b.Int() }
    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
        less = func(a, b reflect.Value) bool { return a.Uint() < b.Uint() }
    case reflect.Float32, reflect.Float64:
        less = func(a, b reflect.Value) bool { return a.Float() < b.Float() }
    case reflect.String:
        less = func(a, b reflect.Value) bool { return a.String() < b.String() }
    case reflect.Bool:
        less = func(a, b reflect.Value) bool { return !a.Bool() && b.Bool() }
    default:
        return fmt.Errorf("field type %s not supported", sf.Type)
    }

    // Sort it!

    sort.Slice(slice, func(i, j int) bool {
        a := v.Index(i)
        b := v.Index(j)
        if indirect {
            a = a.Elem()
            b = b.Elem()
        }
        a = a.FieldByIndex(sf.Index)
        b = b.FieldByIndex(sf.Index)
        return less(a, b)
    })
    return nil
}

将JSON名称映射到一个字段很复杂。在一般情况下,程序需要处理以下情况:嵌入提升的字段和出现的任何冲突、不区分大小写、省略JSON名称等。下面是一个处理问题中的简单情况的函数:

func simpleJSONToFieldName(t reflect.Type, name string) (string, bool) {
    for i := 0; i < t.NumField(); i++ {
        sf := t.Field(i)
        n := strings.Split(sf.Tag.Get("json"), ",")[0]
        if n == name {
            return sf.Name, true
        }
    }
    return "", false
}

下面是如何将它们整合在一起的方法:

 var UserArray []user

 jsonName := request.FormValue("sort")
 fieldName, ok := simpleJSONToFieldName(reflect.TypeOf(user{}), jsonName)
 if !ok {
     // TODO: handle bad input
 }
 if err := sortByField(UserArray, fieldName); err != nil {
     // TODO: handle error
 }