@ -19,22 +19,37 @@ package json
import (
"bytes"
"encoding/csv"
"encoding/json"
"errors"
"fmt"
"strings"
"github.com/bcicen/jstream"
"github.com/minio/minio/pkg/s3select/sql"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
)
// RawJSON is a byte-slice that contains valid JSON
type RawJSON [ ] byte
// MarshalJSON instance for []byte that assumes that byte-slice is
// already serialized JSON
func ( b RawJSON ) MarshalJSON ( ) ( [ ] byte , error ) {
return b , nil
}
// Record - is JSON record.
type Record struct {
data [ ] byte
// Used in Get()
Data [ ] byte
// Used in Set(), Marshal*()
kvs jstream . KVS
}
// Get - gets the value for a column name.
func ( r * Record ) Get ( name string ) ( * sql . Value , error ) {
result := gjson . GetBytes ( r . data , name )
result := gjson . GetBytes ( r . D ata, name )
switch result . Type {
case gjson . Null :
return sql . FromNull ( ) , nil
@ -67,24 +82,33 @@ func (r *Record) Set(name string, value *sql.Value) (err error) {
} else if value . IsNull ( ) {
v = nil
} else if b , ok := value . ToBytes ( ) ; ok {
v = string ( b )
v = RawJSON ( b )
} else {
return fmt . Errorf ( "unsupported sql value %v and type %v" , value , value . GetTypeString ( ) )
}
name = strings . Replace ( name , "*" , "__ALL__" , - 1 )
r . data , err = sjson . SetBytes ( r . data , name , v )
r . kvs = append ( r . kvs , jstream . KV { Key : name , Value : v } )
return err
}
// MarshalCSV - encodes to CSV data.
func ( r * Record ) MarshalCSV ( fieldDelimiter rune ) ( [ ] byte , error ) {
var csvRecord [ ] string
result := gjson . ParseBytes ( r . data )
result . ForEach ( func ( key , value gjson . Result ) bool {
csvRecord = append ( csvRecord , value . String ( ) )
return true
} )
for _ , kv := range r . kvs {
var columnValue string
switch val := kv . Value . ( type ) {
case bool , float64 , int64 , string :
columnValue = fmt . Sprintf ( "%v" , val )
case nil :
columnValue = ""
case RawJSON :
columnValue = string ( [ ] byte ( val ) )
default :
return nil , errors . New ( "Cannot marshal unhandled type" )
}
csvRecord = append ( csvRecord , columnValue )
}
buf := new ( bytes . Buffer )
w := csv . NewWriter ( buf )
@ -103,12 +127,12 @@ func (r *Record) MarshalCSV(fieldDelimiter rune) ([]byte, error) {
// MarshalJSON - encodes to JSON data.
func ( r * Record ) MarshalJSON ( ) ( [ ] byte , error ) {
return r . data , nil
return json . Marshal ( r . kvs )
}
// NewRecord - creates new empty JSON record.
func NewRecord ( ) * Record {
return & Record {
d ata: [ ] byte ( "{}" ) ,
D ata: [ ] byte ( "{}" ) ,
}
}