英文:
having trouble Marshalling struct with pointer
问题
我正在编写一个自定义的Marshalar,将一个结构体转换为一个扁平的列式文件。如果我的结构体没有指针,一切都很顺利。但是当我有一个指向另一个结构体的指针时,我永远无法获得那个数据的Marshalled。我使用标签来指示Marshalar在哪里放置数据以及其长度。
你无法将SubTest3结构体显示为结构体,而是始终显示为指针(变量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)
}
}
})
})
答案1
得分: 1
找到了问题。将代码中的部分修改为:
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论