github.com/goki/ki@v1.1.11/kit/type.go (about) 1 // Copyright (c) 2018, The GoKi Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package kit 6 7 import ( 8 "bytes" 9 "encoding/json" 10 "encoding/xml" 11 "fmt" 12 "reflect" 13 "strings" 14 ) 15 16 // Type provides JSON, XML marshal / unmarshal with encoding of underlying 17 // type name using kit.Types type name registry 18 type Type struct { 19 T reflect.Type 20 } 21 22 // ShortTypeName returns short package-qualified name of the type: package dir + "." + type name 23 func (k Type) ShortTypeName() string { 24 return Types.TypeName(k.T) 25 } 26 27 // String satisfies the stringer interface 28 func String(k Type) string { 29 if k.T == nil { 30 return "nil" 31 } 32 return k.ShortTypeName() 33 } 34 35 // MarshalJSON saves only the type name 36 func (k Type) MarshalJSON() ([]byte, error) { 37 if k.T == nil { 38 b := []byte("null") 39 return b, nil 40 } 41 nm := "\"" + k.ShortTypeName() + "\"" 42 b := []byte(nm) 43 return b, nil 44 } 45 46 // UnmarshalJSON loads the type name and looks it up in the Types registry of type names 47 func (k *Type) UnmarshalJSON(b []byte) error { 48 if bytes.Equal(b, []byte("null")) { 49 k.T = nil 50 return nil 51 } 52 tn := string(bytes.Trim(bytes.TrimSpace(b), "\"")) 53 // fmt.Printf("loading type: %v", tn) 54 typ := Types.Type(tn) 55 if typ == nil { 56 return fmt.Errorf("Type UnmarshalJSON: Types type name not found: %v", tn) 57 } 58 k.T = typ 59 return nil 60 } 61 62 // todo: try to save info as an attribute within a single element instead of 63 // full start/end 64 65 // MarshalXML saves only the type name 66 func (k Type) MarshalXML(e *xml.Encoder, start xml.StartElement) error { 67 tokens := []xml.Token{start} 68 if k.T == nil { 69 tokens = append(tokens, xml.CharData("null")) 70 } else { 71 tokens = append(tokens, xml.CharData(k.ShortTypeName())) 72 } 73 tokens = append(tokens, xml.EndElement{start.Name}) 74 for _, t := range tokens { 75 err := e.EncodeToken(t) 76 if err != nil { 77 return err 78 } 79 } 80 err := e.Flush() 81 if err != nil { 82 return err 83 } 84 return nil 85 } 86 87 // UnmarshalXML loads the type name and looks it up in the Types registry of type names 88 func (k *Type) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { 89 t, err := d.Token() 90 if err != nil { 91 return err 92 } 93 ct, ok := t.(xml.CharData) 94 if ok { 95 tn := string(bytes.TrimSpace([]byte(ct))) 96 if tn == "null" { 97 k.T = nil 98 } else { 99 // fmt.Printf("loading type: %v\n", tn) 100 typ := Types.Type(tn) 101 if typ == nil { 102 return fmt.Errorf("Type UnmarshalXML: Types type name not found: %v", tn) 103 } 104 k.T = typ 105 } 106 t, err := d.Token() 107 if err != nil { 108 return err 109 } 110 et, ok := t.(xml.EndElement) 111 if ok { 112 if et.Name != start.Name { 113 return fmt.Errorf("Type UnmarshalXML: EndElement: %v does not match StartElement: %v", et.Name, start.Name) 114 } 115 return nil 116 } 117 return fmt.Errorf("Type UnmarshalXML: Token: %+v is not expected EndElement", et) 118 } 119 return fmt.Errorf("Type UnmarshalXML: Token: %+v is not expected EndElement", ct) 120 } 121 122 // StructTags returns a map[string]string of the tag string from a reflect.StructTag value 123 // e.g., from StructField.Tag 124 func StructTags(tags reflect.StructTag) map[string]string { 125 if len(tags) == 0 { 126 return nil 127 } 128 flds := strings.Fields(string(tags)) 129 smap := make(map[string]string, len(flds)) 130 for _, fld := range flds { 131 cli := strings.Index(fld, ":") 132 if cli < 0 || len(fld) < cli+3 { 133 continue 134 } 135 vl := strings.TrimSuffix(fld[cli+2:], `"`) 136 smap[fld[:cli]] = vl 137 } 138 return smap 139 } 140 141 // StringJSON returns a JSON representation of item, as a string 142 // e.g., for printing / debugging etc. 143 func StringJSON(it interface{}) string { 144 b, _ := json.MarshalIndent(it, "", " ") 145 return string(b) 146 }