Dynami c структура в параметре в golang - PullRequest
0 голосов
/ 27 января 2020

Мне нужно обработать много файлов, которые различаются по домену, но все они имеют фиксированные позиции строки

Я создаю структуры для доменов с тегами для начальной позиции и конечной позиции в строке

type IP0059T1 struct {
    TableID                            string `startpos:"1" endpos:"8"`
    EffectiveDateTime                  string `startpos:"11" endpos:"11"`
}

type IP0059T2 struct {
    TableID                            string `startpos:"1" endpos:"8"`
    SequenceNumber                  string `startpos:"11" endpos:"14"`
}

И я создаю метод, который работает нормально для одной таблицы

func (s *Service) GetIP0059T01(bundleURI string) ([]IP0059T1, error) {
    reader := getReader(bundleURI)

    var items []IP0059T1
    err = patterns.Each(reader, func(e *contract.Entry) error {
        line := string(e.Data)
        var item = new(IP0059T1)
        structName := reflect.TypeOf(item).Name()
        structValues := reflect.ValueOf(item).Elem()

        for i := 0; i < structValues.NumField(); i++ { // iterates through every struct type field
            field := structValues.Field(i) // returns the content of the struct type field
            value, _ := getValue(line, structValues.Type().Field(i), structName)
            _ = s.SetValue(field, structValues, i, structName, value)
        }

        items = append(items, *item)
        return nil
    })
    if err != nil {
        return nil, err
    }
    return items, nil
}

setValue использует отражение

func setValue(field reflect.Value, structValues reflect.Value, i int, structName string, value string) error {
    if field.Kind() != reflect.String {
        return &FieldNotStringError{Field: structValues.Type().Field(i).Name, Struct: structName}
    }
    field.SetString(value)
    return nil
}

Также getValue использует отражение

func getValue(line string, field reflect.StructField, structName string) (string, error) {
    startPosition, _ := strconv.Atoi(field.Tag.Get("startpos"))
    endPosition, _ := strconv.Atoi(field.Tag.Get("endpos"))

    return line[(startPosition - 1):(endPosition)], nil
}

Итак, есть ли Чтобы преобразовать метод GetIP0059T01 в метод generi c с помощью get uri и типа, и вернуть массив интерфейса, который может быть преобразован в массив типа, который я передаю? В принципе, я хочу что-то вроде дженери c

Ответы [ 2 ]

2 голосов
/ 27 января 2020

Вы можете сделать это так же, как это делает стандартная библиотека Go, например, в encoding/json, которая должна принимать значение, которое будет заполнено в качестве параметра. Ваш «generi c» будет выглядеть примерно так:

GetValues(bundleURI string, dest interface{}) (error)

Где dest, как ожидается, будет указателем на фрагмент любого типа, который предполагается десериализовать, например:

var v []IP0059T1
err = GetValues(myURI, &v)

Затем в GetValues вы можете использовать reflect.Type.Elem(), чтобы получить тип элементов среза, reflect.New(), чтобы создать новые экземпляры этого типа, и reflect.Append(), чтобы добавить их непосредственно в dest. Это сохраняет безопасность типов, в то же время позволяя некоторую меру общего программирования c.

2 голосов
/ 27 января 2020

Похоже, что только два места, где вам действительно нужен тип, это когда вы выделяете его и когда добавляете его в массив. Таким образом, вы можете изменить функцию для получения двух обратных вызовов:

func GetStuff(bundleURI string, newItem func() interface{},collect func(interface{})) error {

   // instead of var item = new(IP0059T1)
   var item = newItem()

   ...

   // instead of items = append(items, *item)
   collect(item)


}

И вызывать функцию с помощью:

items:=make([]Item,0)
GetStuff(uri, func() interface{} {return new(Item)}, 
func(item interface{}) {items=append(items,*item.(*Item))})
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...