Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions homeworks/9_generics_and_reflection/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Домашнее задание №9
В домашнем задании нужно реализовать сериализацию объекта структуры данных в `.properties` формат с использованием тегов структур и рефлексии.

При сериализации данных в некоторых случаях могут возникнуть проблемы с пустыми значениями. Например, если у вас есть структура, в которой некоторые поля могут быть не заполнены, и вы сериализуете, то в результате получится объект с пустыми полями. Если это не является ожидаемым поведением, то в нашей реализации можно будет использовать тег `omitempty`, чтобы пропустить пустые поля при сериализации.

Стуктура для сериализации в `.properties` формат (поподробнее с `.properties` форматом можно ознакомиться [здесь](https://ru.wikipedia.org/wiki/Properties_)):

```go
type Person struct {
Name string `properties:"name"`
Address string `properties:"address,omitempty"`
Age int `properties:"age"`
Married bool `properties:"married"`
}
```

Для выполнения домашнего задания подготовлен шаблон кода и основные тесты, которую помогут проверить корректность реализации конвертации. Шаблона доступен по [ссылке](https://github.com/Balun-courses/deep_go/blob/master/homework/generics_and_reflection/homework_test.go).

Задание со звездочкой
Выполнять необязательно, но если вы хотите, можете попробовать реализовать обощенную функцию сериализации, которая сможет работать не только со структорой `Person`.
117 changes: 117 additions & 0 deletions homeworks/9_generics_and_reflection/homework_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package main

import (
"fmt"
"reflect"
"strings"
"testing"

"github.com/stretchr/testify/assert"
)

// go test -v homework_test.go

type Person struct {
Name string `properties:"name"`
Address string `properties:"address,omitempty"`
Age int `properties:"age"`
Married bool `properties:"married"`
}

type ParsedTag struct {
name string
omitempty bool
}

const (
propertiesTag = "properties"
emptyTag = "omitempty"
)

func Serialize(object any) string {
var sb strings.Builder
t := reflect.TypeOf(object)
v := reflect.ValueOf(object)
for i := 0; i < v.NumField(); i++ {
fieldType := t.Field(i)
fieldValue := v.Field(i)

tag, ok := fieldType.Tag.Lookup(propertiesTag)
if !ok {
continue
}

parsedTag := parseTag(tag)

if fieldValue.IsZero() && parsedTag.omitempty {
continue
}

if !fieldValue.CanInterface() {
continue
}
value := fieldValue.Interface()

if i > 0 {
sb.WriteByte('\n')
}
// sb.WriteString(fieldType.Name)
sb.WriteString(parsedTag.name)
sb.WriteByte('=')
fmt.Fprint(&sb, value)
}

return sb.String()
}

func parseTag(tag string) ParsedTag {
parsedTag := ParsedTag{}
parts := strings.Split(tag, ",")

for _, v := range parts {
switch v {
case emptyTag:
parsedTag.omitempty = true
default:
parsedTag.name = v
}

}

return parsedTag
}

func TestSerialization(t *testing.T) {
tests := map[string]struct {
person Person
result string
}{
"test case with empty fields": {
result: "name=\nage=0\nmarried=false",
},
"test case with fields": {
person: Person{
Name: "John Doe",
Age: 30,
Married: true,
},
result: "name=John Doe\nage=30\nmarried=true",
},
"test case with omitempty field": {
person: Person{
Name: "John Doe",
Age: 30,
Married: true,
Address: "Paris",
},
result: "name=John Doe\naddress=Paris\nage=30\nmarried=true",
},
}

for name, test := range tests {
t.Run(name, func(t *testing.T) {
result := Serialize(test.person)
assert.Equal(t, test.result, result)
})
}
}