Go에서 JSON 가독성 향상
{"foo":"bar"}
들여쓰기는 왼쪽 패딩이 있는 별도의 줄에 구문 단위를 배치하여 사람이 읽을 수 있도록 JSON 형식을 개선하는 데 도움이 됩니다.
[
{
"comment": "empty list, empty docs",
"doc": {},
"patch": [],
"expected": {}
},
{
"comment": "empty patch list",
"doc": {
"foo": 1
},
"patch": [],
"expected": {
"foo": 1
}
}
]
평균 크기의 문서에서는 잘 작동하지만 계층이 풍부한 큰 문서에서는 불편한 경향이 있습니다. 더 큰 문서는 너무 많은 줄을 사용하여 렌더링되며 읽기 위해 상당한 수직 스크롤이 필요합니다. 작은 요소라도 줄을 긋기 때문에 종종 화면 공간의 작은 부분만 데이터에 활용됩니다.
더 작은 조각은 압축되고 더 큰 조각은 들여쓰기될 때 하이브리드 접근 방식으로 타협에 도달할 수 있습니다.
{
"openapi":"3.0.2",
"info":{"title":"","version":""},
"paths":{
"/test/{in-path}":{
"post":{
"summary":"Title",
"description":"",
"operationId":"name",
"parameters":[
{"name":"in_query","in":"query","schema":{"type":"integer"}},
{"name":"in-path","in":"path","required":true,"schema":{"type":"boolean"}},
{"name":"in_cookie","in":"cookie","schema":{"type":"number"}},
{"name":"X-In-Header","in":"header","schema":{"type":"string"}}
],
"requestBody":{
"content":{
"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/FormDataOpenapiTestInput"}}
}
},
"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{}}}}},
"deprecated":true
}
}
},
"components":{
"schemas":{"FormDataOpenapiTestInput":{"type":"object","properties":{"in_form_data":{"type":"string"}}}}
}
}
Go에서 이러한 마샬러를 구현해 봅시다. JSON 문서를 살펴보고 이미 작은 잎을 찾을 때까지 들여쓰기를 적용할 수 있습니다. 그런 다음 해당 잎을 컴팩트한 형태로 렌더링할 수 있습니다.
문서 너비 제한으로 줄 길이를 사용할 수 있습니다. 패딩된 JSON 라인이 라인 길이 제한보다 짧으면 압축 형식을 사용할 수 있습니다.
JSON 사양은 속성 순서에 대한 의미 체계를 정의하지 않지만 가독성을 위해 원래 순서를 유지하는 것이 중요합니다. 이를 위해 github.com/iancoleman/orderedmap을 사용할 수 있습니다.
// MarshalIndentCompact applies indentation for large chunks of JSON and uses compact format for smaller ones.
//
// Line length limits indented width of JSON structure, does not apply to long distinct scalars.
// This function is not optimized for performance, so it might be not a good fit for high load scenarios.
func MarshalIndentCompact(v interface{}, prefix, indent string, lineLen int) ([]byte, error) {
b, err := json.Marshal(v)
if err != nil {
return nil, err
}
// Return early if document is small enough.
if len(b) <= lineLen {
return b, nil
}
m := orderedmap.New()
// Create a temporary JSON object to make sure it can be unmarshaled into a map.
tmpMap := append([]byte(`{"t":`), b...)
tmpMap = append(tmpMap, '}')
// Unmarshal JSON payload into ordered map to recursively walk the document.
err = json.Unmarshal(tmpMap, m)
if err != nil {
return nil, err
}
i, ok := m.Get("t")
if !ok {
return nil, orderedmap.NoValueError
}
// Create first level padding.
pad := append([]byte(prefix), []byte(indent)...)
// Call recursive function to walk the document.
return marshalIndentCompact(i, indent, pad, lineLen)
}
이제
marshalIndentCompact
에서 type switch을 사용하여 배열과 객체에서 더 깊이 재귀할 수 있습니다.func marshalIndentCompact(doc interface{}, indent string, pad []byte, lineLen int) ([]byte, error) {
// Build compact JSON for provided sub document.
compact, err := json.Marshal(doc)
if err != nil {
return nil, err
}
// Return compact if it fits line length limit with current padding.
if len(compact)+len(pad) <= lineLen {
return compact, nil
}
// Indent arrays and objects that are too big.
switch o := doc.(type) {
case orderedmap.OrderedMap:
return marshalObject(o, len(compact), indent, pad, lineLen)
case []interface{}:
return marshalArray(o, len(compact), indent, pad, lineLen)
}
// Use compact for scalar values (numbers, strings, booleans, nulls).
return compact, nil
}
배열은
[]interface{}
로 표시되며 모든 항목에 재귀 서식을 적용하기 위해 반복할 수 있습니다.func marshalArray(o []interface{}, compactLen int, indent string, pad []byte, lineLen int) ([]byte, error) {
// Allocate result with a size of compact form, because it is impossible to make result shorter.
res := append(make([]byte, 0, compactLen), '[', '\n')
for i, val := range o {
// Build item value with an increased padding.
jsonVal, err := marshalIndentCompact(val, indent, append(pad, []byte(indent)...), lineLen)
if err != nil {
return nil, err
}
// Add item JSON with current padding.
res = append(res, pad...)
res = append(res, jsonVal...)
if i == len(o)-1 {
// Close array at last item.
res = append(res, '\n')
// Strip one indent from a closing bracket.
res = append(res, pad[len(indent):]...)
res = append(res, ']')
} else {
// Add colon and new line after an item.
res = append(res, ',', '\n')
}
}
return res, nil
}
속성 순서를 유지하기 위해 순서가 지정된 키를 사용하여 반복할 수 있는 JSON 개체는
orderedmap.OrderedMap
로 사용할 수 있습니다. 모든 속성 값은 들여쓰기를 통해 재귀적으로 처리됩니다.func marshalObject(o orderedmap.OrderedMap, compactLen int, indent string, pad []byte, lineLen int) ([]byte, error) {
// Allocate result with a size of compact form, because it is impossible to make result shorter.
res := append(make([]byte, 0, compactLen), '{', '\n')
// Iterate object using keys slice to preserve properties order.
keys := o.Keys()
for i, k := range keys {
val, ok := o.Get(k)
if !ok {
return nil, orderedmap.NoValueError
}
// Build item value with an increased padding.
jsonVal, err := marshalIndentCompact(val, indent, append(pad, []byte(indent)...), lineLen)
if err != nil {
return nil, err
}
// Marshal key as JSON string.
kj, err := json.Marshal(k)
if err != nil {
return nil, err
}
// Add key JSON with current padding.
res = append(res, pad...)
res = append(res, kj...)
res = append(res, ':')
// Add value JSON to the same line.
res = append(res, jsonVal...)
if i == len(keys)-1 {
// Close object at last property.
res = append(res, '\n')
// Strip one indent from a closing bracket.
res = append(res, pad[len(indent):]...)
res = append(res, '}')
} else {
// Add colon and new line after a property.
res = append(res, ',', '\n')
}
}
return res, nil
}
이 API는 github.com/swaggest/assertjson 라이브러리와 CLI tool에서 사용할 수 있습니다. 테스트 및 기타 경우에 대한 JSON 가독성을 개선하는 데 도움이 되기를 바랍니다.
읽어 주셔서 감사합니다.
Reference
이 문제에 관하여(Go에서 JSON 가독성 향상), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/vearutop/reducing-indented-json-height-in-go-40nd텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)