# Literal로 crdt.Object 생성하기 스팩
https://codepair.yorkie.dev/fduqs
## json.Array 생성
---
#### 1. SetNewArray method를 이용한 빈 배열 생성
```go=
root.SetNewArray("k1")
```
#### 2. 특정 타입(user defined struct 포함)으로 만들어진 slice, array로 json.Array 선언
> primitive, interface{ }, Counter, Text, Tree, []map[string]interface{ }, user defined struct
``` go=
// int
root.SetNewArray("k1", []int{0, 1, 2})
// void interface
root.SetNewArray("k2", []interface{}{0,1,2})
// Counter
cnt := json.NewCounter(0, crdt.LongCnt)
root.SetNewArray("counters", []json.Counter{cnt, cnt, cnt})
// User defined struct
type T1 struct {
msg string
Msg string
}
root.SetNewArray("structs", []T1{{"hello", "Hello"}, {"world", "World"}})
// typed slice in typed slice
arr := []int{1, 2, 3}
root.SetNewArray("arrays", []([]int){arr, arr})
// typed slice in typed array
arr := [3]int{1, 2, 3}
root.SetNewArray("arrays", []([3]int){arr})
```
> 현재 array 같은 경우 slice로 변환되어 처리된다. 성능상 오버헤드가 있을 수 있다.
## json.Object 생성
---
#### 1. SetNewObject method를 이용한 빈 객체 생성
```go=
root.SetNewObject("obj")
```
#### 2. map을 이용해서 json object 생성
```go=
root.SetNewObject("obj", map[string]interface{}{ // 1: object
"str": "v", // 1: string
"arr": []interface{}{1, "str"}, // 3: array, 1, "str"
"obj": map[string]interface{}{"key3": 42.2}, // 2: object, 42.2
"cnt": json.NewCounter(0, crdt.LongCnt), // 1: counter
"txt": json.NewText(), // 1: text
"tree": json.NewTree(&json.TreeNode{ // 1: tree
Type: "doc",
Children: []json.TreeNode{{
Type: "p", Children: []json.TreeNode{{Type: "text", Value: "ab"}},
}},
}),
})
```
#### 3. user defined struct 로 json object 생성
```go
root.SetNewObject("obj", struct{ M string }{M: text})
```
- exposed field만 json.Object에 추가된다.
> struct와 map 형태는 주소 또한 받을 수 있다.
## Struct field tag
---
- user defined struct로 json.Object, json.Array를 초기화 할 때, tag를 이용할 수 있다.
- tag는 아래의 포맷을 따른다. 콤마를 기준으로 앞은 name, 뒤는 option이다.
```format
`yorkie:(user defined name || - ),options`
```
> name에 대해서는 trim 작업 x -> 공백 허용
options는 trim을 통해 공백 제거
```go=
type Temp struct{
// user defined name, -
K1 string `yorkie:"-"` // case1
K2 string `yorkie:"k2"` // case2
// options
K3 string `yorkie:",omitEmpty"` // case3
}
```
### 1. - always omit
- `-` 로 태그된 필드는 crdt.Object에 포함되지 않는다.
- 뒤에 옵션이 붙으면 -를 user defined name으로 간주
```go=
struct {
M1 string `yorkie:"-"`
M2 string
}{M1: str, M2: str}
```
```shell=
{"obj":{"M2":"foo"}}
```
### 2. field name mapping
- 기본적으로 exposed만 변환하기에 키가 대문자로 시작한다.
- 태그를 이용해 미리 정의된 이름으로 key 설정 가능하다.
```go=
struct {
M1 string `yorkie:"m1"`
M2 string `yorkie:"m2"`
{M1: str, M2: str}
```
```shell=
{"obj":{"m1":"foo","m2":"foo"}}
```
### 3. omitEmpty
- 값이 없다면 출력되지 않음(초기화를 안해 zero value인 케이스)
- nil이 아닌 빈 배열인 경우도 생략
> "tagName,omitEmpty" 는 정상 작동
> "tagName, omitEmpty" 한 칸 뛰어쓰면 작동 안함 (json marshal 마찬가지)
> trim 처리로 해결
```go=
// zero value
struct {
M1 string `yorkie:",omitEmpty"`
M2 string `yorkie:",omitEmpty"`
}{M1: str}
```
```shell=
{"obj":{"M1":"foo"}}
```
```go=
// empty array
struct {
M1 []int `yorkie:",omitEmpty"`
}{M1: []int{}}
```
```shell=
{"obj":{}}
```
## 추가 기능 후보
---
- case4 // Counter tag rather than NewCounter ❔
- int값을 던져주고, 이를 Counter 객체로 만들도록
- ( string 값을 던져주고, 이를 Text 객체로 ) / 기존 string을 받아 text를 초기화 생성하는 방법이 없기에 같이 다루어야한다.
- case5 // int, float -> string 으로 전환
- int 값을 넣었으나, 인코딩할 때 스트링으로 전환
> sometimes used when communicating with JavaScript programs https://pkg.go.dev/encoding/json#Marshal
## Nil, Zerovalue, Empty 스펙
---
- zero value
- 기본적으로 struct의 field 값을 assign 하지 않으면 default값이 들어간다.
- nil 은 포인터, 인터페이스, 맵, 슬라이스, 채널, 함수의 zero value이다.
- nil slice -> null
- empty slice -> [ ]
```go
{"zeroValue int struct", struct{ M int }{}, `{"obj":{"M":0}}`},
{"zeroValue string struct", struct{ M string }{}, `{"obj":{"M":""}}`},
```
```go
{"zeroValue bytes struct", struct{ M []byte }{M: nil}, `{"obj":{"M":""}}`},
{"empty bytes struct", struct{ M []byte }{M: []byte{}}, `{"obj":{"M":""}}`},
바이트 배열인 경우 nil과 empty 모두 ""으로 표현된다.
{"zeroValue array struct", struct{ M []int }{M: nil}, `{"obj":{"M":[]}}`},
{"empty array struct", struct{ M []int }{M: []int{}}, `{"obj":{"M":[]}}`},
```
빈 배열과 nil 배열 모두 [ ]으로 표현된다.
- 다음은 구조체에서 각 필드를 선언했을 때, 기본 값이 다뤄지는 방법을 정리
| Type | zero value(not initialized) | Empty value(initialized but empty) | Initialize method |
| --------------- | --------------- | --------------- | --------------- |
| int | 0 |
| String | "" | "" |""
| []byte | "" | "" | []byte
| json.Array | [ ] | [ ] | []int{}
| json.Counter | 0 | | NewCounter|
| json.Tree | {"type":"root","children":[]} | {"type":"root","children":[]} | NewTree
| json.Text | [ ] | [ ] | NewText
| json.Object | { } | |
| pointer | null | | nil
> Blank means there is no way
## Pointer 스펙
---
- slice, array나 struct안 포인터는 포인터가 가리키는 객체를 생성한다.
- 다만 json.Text, Counter, Tree, Object는 지원하지 않는다. (주소를 얻는 것이 불가능)
```go=
// available
{"struct pointers", []*T1{&t1, &t1}, `[{"M":"a"},{"M":"a"}]`, 5},
{"array pointers", []*[3]int{&arr, &arr}, `[[1,2,3],[1,2,3]]`, 9},
// does not support
{"Counter Pointer", []*json.Counter{&json.NewCounter(1, crdt.LongCnt)}, `[1]`}
```
- 포인터가 nil이라면 json.primitive의 null로 바뀌어 생성된다.
```go=
{"nested &struct with nil", struct{ M *T1 }{M: nil}, `{"obj":{"M":null}}`, 2},
```
- json.Object 생성 시 struct와 map 형태는 주소 또한 받을 수 있다.
- 마찬가지로 값으로 바꾸어 json.Object가 생성된다.
## Unsupported Values
---
If these values are included in the structure array, panic will occur.
```go=
pointerCycle,
pointerCycleIndirect,
mapCycle,
sliceCycle,
recursiveSliceCycle,
```