generated from dellevin/template
1
This commit is contained in:
60
node_modules/flatted/golang/README.md
generated
vendored
Normal file
60
node_modules/flatted/golang/README.md
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
# flatted (Go)
|
||||
|
||||
A super light and fast circular JSON parser.
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/WebReflection/flatted/golang/pkg/flatted"
|
||||
)
|
||||
|
||||
type Group struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Name string `json:"name"`
|
||||
Friend *User `json:"friend"`
|
||||
Group *Group `json:"group"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
group := &Group{Name: "Developers"}
|
||||
alice := &User{Name: "Alice", Group: group}
|
||||
bob := &User{Name: "Bob", Group: group}
|
||||
|
||||
alice.Friend = bob
|
||||
bob.Friend = alice // Circular reference
|
||||
|
||||
// Stringify Alice
|
||||
s, _ := flatted.Stringify(alice)
|
||||
fmt.Println(s)
|
||||
// Output: [{"name":"Alice","friend":"1","group":"2"},{"name":"Bob","friend":"0","group":"2"},{"name":"Developers"}]
|
||||
|
||||
// Flattening in action:
|
||||
// Index "0" is Alice, Index "1" is Bob, Index "2" is the shared Group.
|
||||
|
||||
// Parse back into a generic map structure
|
||||
res, _ := flatted.Parse(s)
|
||||
aliceMap := res.(map[string]any)
|
||||
fmt.Println(aliceMap["name"]) // Alice
|
||||
}
|
||||
```
|
||||
|
||||
## CLI
|
||||
|
||||
Build the binary using the provided Makefile:
|
||||
|
||||
```bash
|
||||
make build
|
||||
```
|
||||
|
||||
Then use it to parse flatted JSON from stdin:
|
||||
|
||||
```bash
|
||||
echo '[{"a":"1"},"b"]' | ./flatted
|
||||
```
|
||||
277
node_modules/flatted/golang/pkg/flatted/flatted.go
generated
vendored
Normal file
277
node_modules/flatted/golang/pkg/flatted/flatted.go
generated
vendored
Normal file
@@ -0,0 +1,277 @@
|
||||
package flatted
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// flattedIndex is a internal type used to distinguish between
|
||||
// actual strings and flatted indices during the reconstruction phase.
|
||||
type flattedIndex string
|
||||
|
||||
// Stringify converts a Go value into a specialized flatted JSON string.
|
||||
func Stringify(value any, replacer any, space any) (string, error) {
|
||||
knownKeys := []any{}
|
||||
knownValues := []string{}
|
||||
input := []any{}
|
||||
|
||||
index := func(v any) string {
|
||||
input = append(input, v)
|
||||
idx := strconv.Itoa(len(input) - 1)
|
||||
knownKeys = append(knownKeys, v)
|
||||
knownValues = append(knownValues, idx)
|
||||
return idx
|
||||
}
|
||||
|
||||
relate := func(v any) any {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
rv := reflect.ValueOf(v)
|
||||
kind := rv.Kind()
|
||||
if kind == reflect.String || kind == reflect.Slice || kind == reflect.Map || kind == reflect.Ptr {
|
||||
for i, k := range knownKeys {
|
||||
if kind == reflect.String {
|
||||
if k == v {
|
||||
return knownValues[i]
|
||||
}
|
||||
} else {
|
||||
rk := reflect.ValueOf(k)
|
||||
if rk.Kind() == kind && rk.Pointer() == rv.Pointer() {
|
||||
return knownValues[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
return index(v)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
transform := func(v any) any {
|
||||
rv := reflect.ValueOf(v)
|
||||
if !rv.IsValid() {
|
||||
return nil
|
||||
}
|
||||
if _, ok := v.(json.Marshaler); ok {
|
||||
return v
|
||||
}
|
||||
// Dereference pointers to process the underlying Slice, Map, or Array
|
||||
for rv.Kind() == reflect.Ptr && !rv.IsNil() {
|
||||
rv = rv.Elem()
|
||||
}
|
||||
switch rv.Kind() {
|
||||
case reflect.Slice, reflect.Array:
|
||||
res := make([]any, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
res[i] = relate(rv.Index(i).Interface())
|
||||
}
|
||||
return res
|
||||
case reflect.Map:
|
||||
res := make(map[string]any)
|
||||
keys := rv.MapKeys()
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
return keys[i].String() < keys[j].String()
|
||||
})
|
||||
|
||||
whitelist, isWhitelist := replacer.([]string)
|
||||
for _, key := range keys {
|
||||
kStr := key.String()
|
||||
if isWhitelist {
|
||||
found := false
|
||||
for _, w := range whitelist {
|
||||
if w == kStr {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
}
|
||||
res[kStr] = relate(rv.MapIndex(key).Interface())
|
||||
}
|
||||
return res
|
||||
case reflect.Struct:
|
||||
res := make(map[string]any)
|
||||
t := rv.Type()
|
||||
for i := 0; i < rv.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
if field.PkgPath != "" {
|
||||
continue
|
||||
}
|
||||
name := field.Name
|
||||
if tag := field.Tag.Get("json"); tag != "" {
|
||||
name = strings.Split(tag, ",")[0]
|
||||
}
|
||||
res[name] = relate(rv.Field(i).Interface())
|
||||
}
|
||||
return res
|
||||
default:
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
index(value)
|
||||
output := []any{}
|
||||
for i := 0; i < len(input); i++ {
|
||||
output = append(output, transform(input[i]))
|
||||
}
|
||||
|
||||
var b []byte
|
||||
var err error
|
||||
indent := ""
|
||||
if s, ok := space.(string); ok {
|
||||
indent = s
|
||||
} else if i, ok := space.(int); ok {
|
||||
indent = strings.Repeat(" ", i)
|
||||
}
|
||||
|
||||
if indent != "" {
|
||||
b, err = json.MarshalIndent(output, "", indent)
|
||||
} else {
|
||||
b, err = json.Marshal(output)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
// Parse converts a specialized flatted string into a Go value.
|
||||
func Parse(text string, reviver func(key string, value any) any) (any, error) {
|
||||
var jsonInput []any
|
||||
if err := json.Unmarshal([]byte(text), &jsonInput); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var wrap func(any) any
|
||||
wrap = func(v any) any {
|
||||
if s, ok := v.(string); ok {
|
||||
return flattedIndex(s)
|
||||
}
|
||||
if arr, ok := v.([]any); ok {
|
||||
for i, item := range arr {
|
||||
arr[i] = wrap(item)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
if m, ok := v.(map[string]any); ok {
|
||||
for k, item := range m {
|
||||
m[k] = wrap(item)
|
||||
}
|
||||
return m
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
wrapped := make([]any, len(jsonInput))
|
||||
for i, v := range jsonInput {
|
||||
wrapped[i] = wrap(v)
|
||||
}
|
||||
|
||||
input := make([]any, len(wrapped))
|
||||
for i, v := range wrapped {
|
||||
if fi, ok := v.(flattedIndex); ok {
|
||||
input[i] = string(fi)
|
||||
} else {
|
||||
input[i] = v
|
||||
}
|
||||
}
|
||||
|
||||
if len(input) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
value := input[0]
|
||||
rv := reflect.ValueOf(value)
|
||||
if rv.IsValid() && (rv.Kind() == reflect.Slice || rv.Kind() == reflect.Map) {
|
||||
set := make(map[uintptr]bool)
|
||||
set[rv.Pointer()] = true
|
||||
res := loop(value, input, set)
|
||||
if reviver != nil {
|
||||
return revive("", res, reviver), nil
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
if reviver != nil {
|
||||
return reviver("", value), nil
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func revive(key string, value any, reviver func(k string, v any) any) any {
|
||||
if arr, ok := value.([]any); ok {
|
||||
for i, v := range arr {
|
||||
arr[i] = revive(strconv.Itoa(i), v, reviver)
|
||||
}
|
||||
} else if m, ok := value.(map[string]any); ok {
|
||||
keys := make([]string, 0, len(m))
|
||||
for k := range m {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
m[k] = revive(k, m[k], reviver)
|
||||
}
|
||||
}
|
||||
return reviver(key, value)
|
||||
}
|
||||
|
||||
func loop(value any, input []any, set map[uintptr]bool) any {
|
||||
if arr, ok := value.([]any); ok {
|
||||
for i, v := range arr {
|
||||
if fi, ok := v.(flattedIndex); ok {
|
||||
idx, _ := strconv.Atoi(string(fi))
|
||||
arr[i] = ref(input[idx], input, set)
|
||||
}
|
||||
}
|
||||
return arr
|
||||
}
|
||||
if m, ok := value.(map[string]any); ok {
|
||||
for k, v := range m {
|
||||
if fi, ok := v.(flattedIndex); ok {
|
||||
idx, _ := strconv.Atoi(string(fi))
|
||||
m[k] = ref(input[idx], input, set)
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func ref(value any, input []any, set map[uintptr]bool) any {
|
||||
rv := reflect.ValueOf(value)
|
||||
if rv.IsValid() && (rv.Kind() == reflect.Slice || rv.Kind() == reflect.Map) {
|
||||
ptr := rv.Pointer()
|
||||
if !set[ptr] {
|
||||
set[ptr] = true
|
||||
return loop(value, input, set)
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// ToJSON converts a generic value into a JSON serializable object without losing recursion.
|
||||
func ToJSON(value any) (any, error) {
|
||||
s, err := Stringify(value, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var res any
|
||||
err = json.Unmarshal([]byte(s), &res)
|
||||
return res, err
|
||||
}
|
||||
|
||||
// FromJSON converts a previously serialized object with recursion into a recursive one.
|
||||
func FromJSON(value any) (any, error) {
|
||||
b, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Parse(string(b), nil)
|
||||
}
|
||||
Reference in New Issue
Block a user