在用指针编造结构时遇到困难

发布于 2025-01-22 10:09:46 字数 8450 浏览 1 评论 0原文

我正在编写自定义大教堂,以将结构转换为平面柱状文件。如果我的结构没有指针,一切都很好。但是,当我有一个指向另一个结构的指针时,我从来没有得到编组的数据。我正在使用标签来指示马歇尔在何处放置数据及其长度。

package flatparse

import (
    "reflect"

    "github.com/pkg/errors"
)

func Marshal(v interface{}, results *[]byte) error {
    fpTag := &flatparseTag{}

    if reflect.TypeOf(v).Kind() == reflect.Ptr {
        vType := reflect.TypeOf(v).Elem()
        vKind := vType.Kind()

        //Only process if kind is Struct
        if vKind == reflect.Struct {
            //Dereference pointer to struct
            vStruct := reflect.ValueOf(v).Elem()
            maxField := vStruct.NumField()
            //Loop through struct fields/properties
            for i := 0; i < maxField; i++ {
                //Get underlying type of field
                fieldType := vStruct.Field(i).Type()
                fieldTag, tagFlag := vType.Field(i).Tag.Lookup("flatparse")
                if tagFlag {
                    tagParseErr := parseFlatparseTag(fieldTag, fpTag)
                    if tagParseErr != nil {
                        return errors.Wrapf(tagParseErr, "Marshal: Failed to parse field tag %s", fieldTag)
                    }
                    err := writeBasedOnKind(fieldType.Kind(), vStruct.Field(i), results, fpTag)
                    if err != nil {
                        return err
                    }
                }
            }
        }
        return nil
    }
    return errors.Errorf("Marshal: Marshal not complete. %s is not a pointer", reflect.TypeOf(v))
}
package flatparse

import (
    "fmt"
    "reflect"
    "strconv"

    "github.com/pkg/errors"
)

//writeBasedOnKind performs write of field value to byte array based on kind
func writeBasedOnKind(kind reflect.Kind, field reflect.Value, returnData *[]byte, fpTag *flatparseTag) error {
    var err error
    err = nil
    switch kind {
    case reflect.Bool:
        return writeBool(kind, field, returnData, fpTag)
    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
        return writeUint(kind, field, returnData, fpTag)
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
        return writeInt(kind, field, returnData, fpTag)
    case reflect.Float32, reflect.Float64:
        return writeFloat(kind, field, returnData, fpTag)
    case reflect.String:
        return writeString(kind, field, returnData, fpTag)
    case reflect.Struct:
        return Marshal(field.Addr().Interface(), returnData)
    case reflect.Ptr:
        if field.Elem().Kind() == reflect.Struct {
            return Marshal(field.Addr().Interface(), returnData)
        } else {
            return writeBasedOnKind(field.Elem().Kind(), field.Elem(), returnData, fpTag)
        }
    case reflect.Array:
        for i := 0; i < field.Len(); i++ {
            var arrayResults []byte
            err = writeBasedOnKind(field.Type().Elem().Kind(), field.Index(i), &arrayResults, fpTag)
            if err != nil {
                return err
            }
            col := fpTag.col + (fpTag.length * i) - 1
            temp := *returnData
            if len(temp) < col+fpTag.length {
                temp = make([]byte, col+fpTag.length)
                copy(temp, *returnData)
            }
            copy(temp[col:col+len(arrayResults)], arrayResults)
            *returnData = temp
        }
        return nil
    case reflect.Slice:
        if fpTag.occurs < 1 {
            err = errors.Errorf("flatparse.writeBasedOnKind: Occurs clause must be provided when using slice. `flatparse:\"col,len,occurs\"`")
        }
        for i := 0; i < fpTag.occurs; i++ {
            var arrayResults []byte
            err = writeBasedOnKind(field.Type().Elem().Kind(), field.Index(i), &arrayResults, fpTag)
            if err != nil {
                return err
            }
            col := fpTag.col + (fpTag.length * i) - 1
            temp := *returnData
            if len(temp) < col+fpTag.length {
                temp = make([]byte, col+fpTag.length)
                copy(temp, *returnData)
            }
            copy(temp[col:col+len(arrayResults)], arrayResults)
            *returnData = temp
        }
        return nil
    }
    return errors.Wrap(err, "flatparse.writeBasedOnKind: writementError")
}

func writeBool(kind reflect.Kind, field reflect.Value, returnData *[]byte, fpTag *flatparseTag) error {
    v := field.Bool()
    t := "1"
    f := "0"
    if fpTag.override == "boolYN" {
        t = "Y"
        f = "N"
    }
    if v {
        return copyData(returnData, t, fpTag)
    } else {
        return copyData(returnData, f, fpTag)
    }
}

func writeUint(kind reflect.Kind, field reflect.Value, returnData *[]byte, fpTag *flatparseTag) error {
    v := fmt.Sprintf("%0"+strconv.Itoa(fpTag.length)+"d", field.Uint())
    return copyData(returnData, v, fpTag)
}

func writeInt(kind reflect.Kind, field reflect.Value, returnData *[]byte, fpTag *flatparseTag) error {
    v := fmt.Sprintf("%+0"+strconv.Itoa(fpTag.length)+"d", field.Int())
    return copyData(returnData, v, fpTag)
}

func writeFloat(kind reflect.Kind, field reflect.Value, returnData *[]byte, fpTag *flatparseTag) error {
    sfmt := "%+0" + strconv.Itoa(fpTag.length) + "." + strconv.Itoa(fpTag.decimals) + "f"
    v := fmt.Sprintf(sfmt, field.Float())
    return copyData(returnData, v, fpTag)
}

func writeString(kind reflect.Kind, field reflect.Value, returnData *[]byte, fpTag *flatparseTag) error {
    v := fmt.Sprintf("%-"+strconv.Itoa(fpTag.length)+"s", field.String())
    return copyData(returnData, v, fpTag)
}

func copyData(returnData *[]byte, v string, fpTag *flatparseTag) error {
    data := *returnData
    if len(data) < fpTag.col+fpTag.length {
        temp := make([]byte, fpTag.col+fpTag.length-1)
        copy(temp, data)
        data = temp
    }
    copy(data[fpTag.col-1:fpTag.col+fpTag.length-1], v)
    *returnData = data
    return nil
}

测试...

type Test3 struct {
    Field1    string    `flatparse:"col=1,len=1"`
    SubFields *SubTest3 `flatparse:"col=2,len=6"`
}

type SubTest3 struct {
    SubField1 uint `flatparse:"col=2,len=3"`
    SubField2 uint `flatparse:"col=5,len=3"`
}

func TestMarshalPointers(t *testing.T) {
    t.Run("pointer marshal test", func(t *testing.T) {
        subfields := &SubTest3{
            SubField1: 1,
            SubField2: 2,
        }
        test3 := &Test3{
            Field1:    "A",
            SubFields: subfields,
        }

        var results []byte
        err := Marshal(test3, &results)
        if err != nil {
            t.Error(err)
        } else {
            want := "A001002"
            if string(results) != want {
                t.Errorf("error got: %s  want %s", string(results), want)
            }
        }
    })
}

我无法将subtest3 struct显示为结构。在元帅代码中,它总是以指针(变量VKIND)的形式出现。有什么想法我做错了什么?

该测试通过...

type Test1 struct {
    Field1   string      `flatparse:"col=1,len=10"`
    Field2   uint        `flatparse:"col=11,len=1"`
    Field3t  bool        `flatparse:"col=12,len=1,override=boolYN"`
    Field3f  bool        `flatparse:"col=13,len=1,override=boolYN"`
    Field4   int         `flatparse:"col=14,len=4"`
    Field5   float32     `flatparse:"col=18,len=11,dec=2"`
    Field6   float64     `flatparse:"col=29,len=11,dec=2"`
    Segments [5]SubTest1 `flatparse:"col=40,len=6"`
}

type SubTest1 struct {
    Array1 uint `flatparse:"col=1,len=3"`
    Array2 uint `flatparse:"col=4,len=3"`
}

func TestMarshal(t *testing.T) {
    t.Run("fields and array marshal test", func(t *testing.T) {
        test1 := &Test1{
            Field1:  "ABCDEFGHIJ",
            Field2:  1,
            Field3t: true,
            Field3f: false,
            Field4:  3,
            Field5:  12345.67,
            Field6:  -9876.55,
        }
        test1.Segments[0].Array1 = 1
        test1.Segments[0].Array2 = 2
        test1.Segments[1].Array1 = 3
        test1.Segments[1].Array2 = 4

        var results []byte
        err := Marshal(test1, &results)
        if err != nil {
            t.Error(err)
        } else {
            want := "ABCDEFGHIJ1YN+003+0012345.67-0009876.55001002003004000000000000000000"
            if string(results) != want {
                t.Errorf("error got: %s  want %s", string(results), want)
            }
        }
    })
})

I am writing a custom Marshallar to convert a struct to a flat columnar file. If my struct has no pointers, everything works great. but when i have a pointer to another struct, i never get that data Marshalled. I am using tags to instruct the Marshallar where to place the data, as well as its length.

package flatparse

import (
    "reflect"

    "github.com/pkg/errors"
)

func Marshal(v interface{}, results *[]byte) error {
    fpTag := &flatparseTag{}

    if reflect.TypeOf(v).Kind() == reflect.Ptr {
        vType := reflect.TypeOf(v).Elem()
        vKind := vType.Kind()

        //Only process if kind is Struct
        if vKind == reflect.Struct {
            //Dereference pointer to struct
            vStruct := reflect.ValueOf(v).Elem()
            maxField := vStruct.NumField()
            //Loop through struct fields/properties
            for i := 0; i < maxField; i++ {
                //Get underlying type of field
                fieldType := vStruct.Field(i).Type()
                fieldTag, tagFlag := vType.Field(i).Tag.Lookup("flatparse")
                if tagFlag {
                    tagParseErr := parseFlatparseTag(fieldTag, fpTag)
                    if tagParseErr != nil {
                        return errors.Wrapf(tagParseErr, "Marshal: Failed to parse field tag %s", fieldTag)
                    }
                    err := writeBasedOnKind(fieldType.Kind(), vStruct.Field(i), results, fpTag)
                    if err != nil {
                        return err
                    }
                }
            }
        }
        return nil
    }
    return errors.Errorf("Marshal: Marshal not complete. %s is not a pointer", reflect.TypeOf(v))
}
package flatparse

import (
    "fmt"
    "reflect"
    "strconv"

    "github.com/pkg/errors"
)

//writeBasedOnKind performs write of field value to byte array based on kind
func writeBasedOnKind(kind reflect.Kind, field reflect.Value, returnData *[]byte, fpTag *flatparseTag) error {
    var err error
    err = nil
    switch kind {
    case reflect.Bool:
        return writeBool(kind, field, returnData, fpTag)
    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
        return writeUint(kind, field, returnData, fpTag)
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
        return writeInt(kind, field, returnData, fpTag)
    case reflect.Float32, reflect.Float64:
        return writeFloat(kind, field, returnData, fpTag)
    case reflect.String:
        return writeString(kind, field, returnData, fpTag)
    case reflect.Struct:
        return Marshal(field.Addr().Interface(), returnData)
    case reflect.Ptr:
        if field.Elem().Kind() == reflect.Struct {
            return Marshal(field.Addr().Interface(), returnData)
        } else {
            return writeBasedOnKind(field.Elem().Kind(), field.Elem(), returnData, fpTag)
        }
    case reflect.Array:
        for i := 0; i < field.Len(); i++ {
            var arrayResults []byte
            err = writeBasedOnKind(field.Type().Elem().Kind(), field.Index(i), &arrayResults, fpTag)
            if err != nil {
                return err
            }
            col := fpTag.col + (fpTag.length * i) - 1
            temp := *returnData
            if len(temp) < col+fpTag.length {
                temp = make([]byte, col+fpTag.length)
                copy(temp, *returnData)
            }
            copy(temp[col:col+len(arrayResults)], arrayResults)
            *returnData = temp
        }
        return nil
    case reflect.Slice:
        if fpTag.occurs < 1 {
            err = errors.Errorf("flatparse.writeBasedOnKind: Occurs clause must be provided when using slice. `flatparse:\"col,len,occurs\"`")
        }
        for i := 0; i < fpTag.occurs; i++ {
            var arrayResults []byte
            err = writeBasedOnKind(field.Type().Elem().Kind(), field.Index(i), &arrayResults, fpTag)
            if err != nil {
                return err
            }
            col := fpTag.col + (fpTag.length * i) - 1
            temp := *returnData
            if len(temp) < col+fpTag.length {
                temp = make([]byte, col+fpTag.length)
                copy(temp, *returnData)
            }
            copy(temp[col:col+len(arrayResults)], arrayResults)
            *returnData = temp
        }
        return nil
    }
    return errors.Wrap(err, "flatparse.writeBasedOnKind: writementError")
}

func writeBool(kind reflect.Kind, field reflect.Value, returnData *[]byte, fpTag *flatparseTag) error {
    v := field.Bool()
    t := "1"
    f := "0"
    if fpTag.override == "boolYN" {
        t = "Y"
        f = "N"
    }
    if v {
        return copyData(returnData, t, fpTag)
    } else {
        return copyData(returnData, f, fpTag)
    }
}

func writeUint(kind reflect.Kind, field reflect.Value, returnData *[]byte, fpTag *flatparseTag) error {
    v := fmt.Sprintf("%0"+strconv.Itoa(fpTag.length)+"d", field.Uint())
    return copyData(returnData, v, fpTag)
}

func writeInt(kind reflect.Kind, field reflect.Value, returnData *[]byte, fpTag *flatparseTag) error {
    v := fmt.Sprintf("%+0"+strconv.Itoa(fpTag.length)+"d", field.Int())
    return copyData(returnData, v, fpTag)
}

func writeFloat(kind reflect.Kind, field reflect.Value, returnData *[]byte, fpTag *flatparseTag) error {
    sfmt := "%+0" + strconv.Itoa(fpTag.length) + "." + strconv.Itoa(fpTag.decimals) + "f"
    v := fmt.Sprintf(sfmt, field.Float())
    return copyData(returnData, v, fpTag)
}

func writeString(kind reflect.Kind, field reflect.Value, returnData *[]byte, fpTag *flatparseTag) error {
    v := fmt.Sprintf("%-"+strconv.Itoa(fpTag.length)+"s", field.String())
    return copyData(returnData, v, fpTag)
}

func copyData(returnData *[]byte, v string, fpTag *flatparseTag) error {
    data := *returnData
    if len(data) < fpTag.col+fpTag.length {
        temp := make([]byte, fpTag.col+fpTag.length-1)
        copy(temp, data)
        data = temp
    }
    copy(data[fpTag.col-1:fpTag.col+fpTag.length-1], v)
    *returnData = data
    return nil
}

Test...

type Test3 struct {
    Field1    string    `flatparse:"col=1,len=1"`
    SubFields *SubTest3 `flatparse:"col=2,len=6"`
}

type SubTest3 struct {
    SubField1 uint `flatparse:"col=2,len=3"`
    SubField2 uint `flatparse:"col=5,len=3"`
}

func TestMarshalPointers(t *testing.T) {
    t.Run("pointer marshal test", func(t *testing.T) {
        subfields := &SubTest3{
            SubField1: 1,
            SubField2: 2,
        }
        test3 := &Test3{
            Field1:    "A",
            SubFields: subfields,
        }

        var results []byte
        err := Marshal(test3, &results)
        if err != nil {
            t.Error(err)
        } else {
            want := "A001002"
            if string(results) != want {
                t.Errorf("error got: %s  want %s", string(results), want)
            }
        }
    })
}

I cannot get the SubTest3 struct to appear as a struct. In the Marshal code it always appears as a pointer (variable vKind). Any ideas what I am doing wrong?

This test passes...

type Test1 struct {
    Field1   string      `flatparse:"col=1,len=10"`
    Field2   uint        `flatparse:"col=11,len=1"`
    Field3t  bool        `flatparse:"col=12,len=1,override=boolYN"`
    Field3f  bool        `flatparse:"col=13,len=1,override=boolYN"`
    Field4   int         `flatparse:"col=14,len=4"`
    Field5   float32     `flatparse:"col=18,len=11,dec=2"`
    Field6   float64     `flatparse:"col=29,len=11,dec=2"`
    Segments [5]SubTest1 `flatparse:"col=40,len=6"`
}

type SubTest1 struct {
    Array1 uint `flatparse:"col=1,len=3"`
    Array2 uint `flatparse:"col=4,len=3"`
}

func TestMarshal(t *testing.T) {
    t.Run("fields and array marshal test", func(t *testing.T) {
        test1 := &Test1{
            Field1:  "ABCDEFGHIJ",
            Field2:  1,
            Field3t: true,
            Field3f: false,
            Field4:  3,
            Field5:  12345.67,
            Field6:  -9876.55,
        }
        test1.Segments[0].Array1 = 1
        test1.Segments[0].Array2 = 2
        test1.Segments[1].Array1 = 3
        test1.Segments[1].Array2 = 4

        var results []byte
        err := Marshal(test1, &results)
        if err != nil {
            t.Error(err)
        } else {
            want := "ABCDEFGHIJ1YN+003+0012345.67-0009876.55001002003004000000000000000000"
            if string(results) != want {
                t.Errorf("error got: %s  want %s", string(results), want)
            }
        }
    })
})

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

暖伴 2025-01-29 10:09:46

找到了这个问题。更改

return Marshal(field.Addr().Interface(), returnData)

return Marshal(field.Interface(), returnData)

现在所有测试都通过

Found the issue. Changed

return Marshal(field.Addr().Interface(), returnData)

to

return Marshal(field.Interface(), returnData)

Now all tests are passing

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文