Skip to content

Commit 5004667

Browse files
authored
Add tag option for omitting serialization of nil pointer values (#22)
Add omitnilptr tag option for omitting encoding of nil pointers. This prevents panicking when attempting encoding struct with nil pointer fields.
1 parent 7f8a11c commit 5004667

File tree

4 files changed

+84
-13
lines changed

4 files changed

+84
-13
lines changed

README.md

+35-1
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ go get -u github.com/elliotchance/phpserialize
1717
package main
1818

1919
import (
20-
"github.com/elliotchance/phpserialize"
2120
"fmt"
21+
"github.com/elliotchance/phpserialize"
2222
)
2323

2424
func main() {
@@ -35,3 +35,37 @@ func main() {
3535
fmt.Println(in)
3636
}
3737
```
38+
39+
### Using struct field tags for marshalling
40+
41+
```go
42+
package main
43+
44+
import (
45+
"fmt"
46+
"github.com/elliotchance/phpserialize"
47+
)
48+
49+
type MyStruct struct {
50+
// Will be marhsalled as my_purpose
51+
MyPurpose string `php:"my_purpose"`
52+
// Will be marshalled as my_motto, and only if not a nil pointer
53+
MyMotto *string `php:"my_motto,omitnilptr"`
54+
// Will not be marshalled
55+
MySecret string `php:"-"`
56+
}
57+
58+
func main() {
59+
my := MyStruct{
60+
MyPurpose: "No purpose",
61+
MySecret: "Has a purpose",
62+
}
63+
64+
out, err := phpserialize.Marshal(my, nil)
65+
if err != nil {
66+
panic(err)
67+
}
68+
69+
fmt.Println(out)
70+
}
71+
```

serialize.go

+10-5
Original file line numberDiff line numberDiff line change
@@ -161,12 +161,17 @@ func MarshalStruct(input interface{}, options *MarshalOptions) ([]byte, error) {
161161

162162
visibleFieldCount++
163163

164-
// Note: since we can only export fields that are public (start
165-
// with an uppercase letter) we must change it to lower case. If
166-
// you really do want it to be upper case you will have to wait
167-
// for when tags are supported on individual fields.
168-
fieldName := typeOfValue.Field(i).Tag.Get("php")
164+
fieldName, fieldOptions := parseTag(typeOfValue.Field(i).Tag.Get("php"))
165+
166+
if fieldOptions.Contains("omitnilptr") {
167+
if f.Kind() == reflect.Ptr && f.IsNil() {
168+
visibleFieldCount--
169+
continue
170+
}
171+
}
172+
169173
if fieldName == "-" {
174+
visibleFieldCount--
170175
continue
171176
} else if fieldName == "" {
172177
fieldName = lowerCaseFirstLetter(typeOfValue.Field(i).Name)

serialize_test.go

+8-7
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ type structTag struct {
1717
Foo Struct2 `php:"bar"`
1818
Bar int `php:"foo"`
1919
hidden bool
20-
Balu string `php:"baz"`
21-
Ignored string `php:"-"`
20+
Balu string `php:"baz"`
21+
Ignored string `php:"-"`
22+
Nilptr *Struct2 `php:",omitnilptr"`
2223
}
2324

2425
type Struct2 struct {
@@ -134,9 +135,9 @@ var marshalTests = map[string]marshalTest{
134135
},
135136

136137
// encode object (struct with tags)
137-
"structTag{Bar int, Foo Struct2{Qux float64}, hidden bool, Balu string}": {
138-
structTag{Struct2{1.23}, 10, true, "yay", ""},
139-
[]byte("O:9:\"structTag\":4:{s:3:\"bar\";O:7:\"Struct2\":1:{s:3:\"qux\";d:1.23;}s:3:\"foo\";i:10;s:3:\"baz\";s:3:\"yay\";}"),
138+
"structTag{Bar int, Foo Struct2{Qux float64}, hidden bool, Balu string, Nilptr <nil>}": {
139+
structTag{Struct2{1.23}, 10, true, "yay", "", nil},
140+
[]byte("O:9:\"structTag\":3:{s:3:\"bar\";O:7:\"Struct2\":1:{s:3:\"qux\";d:1.23;}s:3:\"foo\";i:10;s:3:\"baz\";s:3:\"yay\";}"),
140141
nil,
141142
},
142143

@@ -166,8 +167,8 @@ func TestMarshal(t *testing.T) {
166167
}
167168

168169
if !reflect.DeepEqual(result, test.output) {
169-
t.Errorf("Expected '%v', got '%v'", string(result),
170-
string(test.output))
170+
t.Errorf("Expected '%v', got '%v'", string(test.output),
171+
string(result))
171172
}
172173
})
173174
}

tags.go

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package phpserialize
2+
3+
import "strings"
4+
5+
type tagOptions string
6+
7+
func parseTag(tag string) (string, tagOptions) {
8+
if i := strings.Index(tag, ","); i != -1 {
9+
return tag[:i], tagOptions(tag[i+1:])
10+
}
11+
return tag, ""
12+
}
13+
14+
func (o tagOptions) Contains(option string) bool {
15+
if len(o) == 0 {
16+
return false
17+
}
18+
s := string(o)
19+
for s != "" {
20+
var next string
21+
i := strings.Index(s, ",")
22+
if i >= 0 {
23+
s, next = s[:i], s[i+1:]
24+
}
25+
if s == option {
26+
return true
27+
}
28+
s = next
29+
}
30+
return false
31+
}

0 commit comments

Comments
 (0)