github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/builtins/types/jsonlines/unmarshal.go (about) 1 package jsonlines 2 3 import ( 4 "bufio" 5 "bytes" 6 "encoding/json" 7 "fmt" 8 9 "github.com/lmorg/murex/lang" 10 "github.com/lmorg/murex/lang/types" 11 ) 12 13 func iface2Str(a []interface{}) []string { 14 s := make([]string, len(a)) 15 for i := range a { 16 s[i] = fmt.Sprint(a[i]) 17 } 18 return s 19 } 20 21 func unmarshal(p *lang.Process) (interface{}, error) { 22 var ( 23 jStruct []interface{} 24 v interface{} 25 jTable [][]string 26 row []interface{} 27 b []byte 28 err error 29 nextEOF bool 30 isStruct bool 31 ) 32 33 scanner := bufio.NewScanner(p.Stdin) 34 for scanner.Scan() { 35 b = scanner.Bytes() 36 if len(bytes.TrimSpace(b)) == 0 { 37 continue 38 } 39 40 isStruct = isStruct || noSquare(b) 41 42 switch { 43 case !isStruct: 44 // is a table 45 46 err = json.Unmarshal(b, &row) 47 if err == nil { 48 jTable = append(jTable, iface2Str(row)) 49 continue 50 } 51 52 isStruct = true 53 for i := range jTable { 54 jStruct = append(jStruct, jTable[i]) 55 } 56 57 fallthrough 58 59 case len(jTable) != 0: 60 // not a row 61 62 for i := range jTable { 63 jStruct = append(jStruct, jTable[i]) 64 } 65 jTable = nil 66 67 fallthrough 68 69 default: 70 // is a struct 71 err = json.Unmarshal(b, &v) 72 switch { 73 case err == nil: 74 jStruct = append(jStruct, v) 75 continue 76 77 case len(jStruct) == 0 && len(b) > 1 && bytes.Contains(b, []byte{'}', '{'}): 78 nextEOF = true 79 continue 80 81 case noQuote(b) && noSquare(b) && noCurly(b): 82 b = append([]byte{'"'}, b...) 83 b = append(b, '"') 84 err = json.Unmarshal(b, &v) 85 if err == nil { 86 jStruct = append(jStruct, v) 87 continue 88 } 89 fallthrough 90 91 default: 92 return jStruct, fmt.Errorf("unable to unmarshal index %d in jsonlines: %s", len(jStruct), err) 93 } 94 } 95 96 } 97 98 if err != nil && nextEOF { 99 return unmarshalNoCrLF(b) 100 } 101 102 err = scanner.Err() 103 if err != nil { 104 return nil, fmt.Errorf("error while unmarshalling a %s: %s", types.JsonLines, err.Error()) 105 } 106 107 if isStruct { 108 return jStruct, err 109 } 110 return jTable, err 111 } 112 113 func unmarshalNoCrLF(b []byte) (interface{}, error) { 114 var ( 115 start int 116 v interface{} 117 err error 118 jStruct []interface{} 119 quoted bool 120 escaped bool 121 ) 122 123 // don't range because we want to skip first byte to avoid ugly bounds 124 // checks within the loop (eg `b[i]-1`). 125 for i := 1; i < len(b)-1; i++ { 126 switch b[i] { 127 case '\\': 128 escaped = !escaped 129 case '"': 130 if escaped { 131 escaped = false 132 } else { 133 quoted = !quoted 134 } 135 case '{': 136 if escaped { 137 escaped = false 138 continue 139 } 140 if !quoted && b[i-1] == '}' { 141 err = json.Unmarshal(b[start:i], &v) 142 if err != nil { 143 return nil, err 144 } 145 jStruct = append(jStruct, v) 146 start = i 147 } 148 default: 149 if escaped { 150 escaped = false 151 } 152 } 153 } 154 155 // catch remainder 156 err = json.Unmarshal(b[start:], &v) 157 if err != nil { 158 return nil, err 159 } 160 jStruct = append(jStruct, v) 161 162 return jStruct, nil 163 }