github.com/goki/ki@v1.1.11/ki/io.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 ki 6 7 import ( 8 "bufio" 9 "bytes" 10 "encoding/json" 11 "encoding/xml" 12 "errors" 13 "fmt" 14 "io" 15 "io/ioutil" 16 "log" 17 "os" 18 "reflect" 19 "strconv" 20 "strings" 21 22 "github.com/goki/ki/kit" 23 ) 24 25 // see https://github.com/goki/ki/wiki/Naming for IO naming conventions 26 27 // TODO: switch to using Decode / Encode instead of 28 // [Un]MarshalJSON which uses byte[] instead of io.Reader / Writer.. 29 30 // JSONTypePrefix is the first thing output in a ki tree JSON output file, 31 // specifying the type of the root node of the ki tree -- this info appears 32 // all on one { } bracketed line at the start of the file, and can also be 33 // used to identify the file as a ki tree JSON file 34 var JSONTypePrefix = []byte("{\"ki.RootType\": ") 35 36 // JSONTypeSuffix is just the } and \n at the end of the prefix line 37 var JSONTypeSuffix = []byte("}\n") 38 39 // WriteJSON writes the tree to an io.Writer, using MarshalJSON -- also 40 // saves a critical starting record that allows file to be loaded de-novo 41 // and recreate the proper root type for the tree. 42 // This calls UniquifyNamesAll because it is essential that names be unique 43 // at this point. 44 func (n *Node) WriteJSON(writer io.Writer, indent bool) error { 45 err := ThisCheck(n) 46 if err != nil { 47 return err 48 } 49 UniquifyNamesAll(n.This()) 50 var b []byte 51 if indent { 52 b, err = json.MarshalIndent(n.This(), "", " ") 53 } else { 54 b, err = json.Marshal(n.This()) 55 } 56 if err != nil { 57 log.Println(err) 58 return err 59 } 60 knm := kit.Types.TypeName(Type(n.This())) 61 tstr := string(JSONTypePrefix) + fmt.Sprintf("\"%v\"}\n", knm) 62 nwb := make([]byte, len(b)+len(tstr)) 63 copy(nwb, []byte(tstr)) 64 copy(nwb[len(tstr):], b) // is there a way to avoid this? 65 _, err = writer.Write(nwb) 66 if err != nil { 67 log.Println(err) 68 } 69 return err 70 } 71 72 // SaveJSON saves the tree to a JSON-encoded file, using WriteJSON. 73 func (n *Node) SaveJSON(filename string) error { 74 fp, err := os.Create(filename) 75 defer fp.Close() 76 if err != nil { 77 log.Println(err) 78 return err 79 } 80 bw := bufio.NewWriter(fp) 81 err = n.WriteJSON(bw, Indent) // use indent by default 82 if err != nil { 83 log.Println(err) 84 return err 85 } 86 err = bw.Flush() 87 if err != nil { 88 log.Println(err) 89 } 90 return err 91 } 92 93 // ReadJSON reads and unmarshals tree starting at this node, from a 94 // JSON-encoded byte stream via io.Reader. First element in the stream 95 // must be of same type as this node -- see ReadNewJSON function to 96 // construct a new tree. Uses ConfigureChildren to minimize changes from 97 // current tree relative to loading one -- wraps UnmarshalJSON and calls 98 // UnmarshalPost to recover pointers from paths. 99 func (n *Node) ReadJSON(reader io.Reader) error { 100 err := ThisCheck(n) 101 if err != nil { 102 log.Println(err) 103 return err 104 } 105 b, err := ioutil.ReadAll(reader) 106 if err != nil { 107 log.Println(err) 108 return err 109 } 110 updt := n.UpdateStart() 111 stidx := 0 112 if bytes.HasPrefix(b, JSONTypePrefix) { // skip type 113 stidx = bytes.Index(b, JSONTypeSuffix) + len(JSONTypeSuffix) 114 } 115 // todo: use json.NewDecoder, Decode instead -- need to deal with TypePrefix etc above 116 err = json.Unmarshal(b[stidx:], n.This()) // key use of this! 117 UnmarshalPost(n.This()) 118 n.SetChildAdded() // this might not be set.. 119 n.UpdateEnd(updt) 120 return err 121 } 122 123 // OpenJSON opens file over this tree from a JSON-encoded file -- see 124 // ReadJSON for details, and OpenNewJSON for opening an entirely new tree. 125 func (n *Node) OpenJSON(filename string) error { 126 fp, err := os.Open(filename) 127 defer fp.Close() 128 if err != nil { 129 log.Println(err) 130 return err 131 } 132 return n.ReadJSON(bufio.NewReader(fp)) 133 } 134 135 // ReadNewJSON reads a new Ki tree from a JSON-encoded byte string, using type 136 // information at start of file to create an object of the proper type 137 func ReadNewJSON(reader io.Reader) (Ki, error) { 138 b, err := ioutil.ReadAll(reader) 139 if err != nil { 140 log.Println(err) 141 return nil, err 142 } 143 if bytes.HasPrefix(b, JSONTypePrefix) { 144 stidx := len(JSONTypePrefix) + 1 145 eidx := bytes.Index(b, JSONTypeSuffix) 146 bodyidx := eidx + len(JSONTypeSuffix) 147 tn := string(bytes.Trim(bytes.TrimSpace(b[stidx:eidx]), "\"")) 148 typ := kit.Types.Type(tn) 149 if typ == nil { 150 return nil, fmt.Errorf("ki.OpenNewJSON: kit.Types type name not found: %v", tn) 151 } 152 root := NewOfType(typ) 153 InitNode(root) 154 155 updt := root.UpdateStart() 156 err = json.Unmarshal(b[bodyidx:], root) 157 UnmarshalPost(root) 158 root.SetChildAdded() // this might not be set.. 159 root.UpdateEnd(updt) 160 return root, nil 161 } 162 return nil, fmt.Errorf("ki.OpenNewJSON -- type prefix not found at start of file -- must be there to identify type of root node of tree") 163 } 164 165 // OpenNewJSON opens a new Ki tree from a JSON-encoded file, using type 166 // information at start of file to create an object of the proper type 167 func OpenNewJSON(filename string) (Ki, error) { 168 fp, err := os.Open(filename) 169 defer fp.Close() 170 if err != nil { 171 log.Println(err) 172 return nil, err 173 } 174 return ReadNewJSON(bufio.NewReader(fp)) 175 } 176 177 // WriteXML writes the tree to an XML-encoded byte string over io.Writer 178 // using MarshalXML. 179 func (n *Node) WriteXML(writer io.Writer, indent bool) error { 180 err := ThisCheck(n) 181 if err != nil { 182 log.Println(err) 183 return err 184 } 185 var b []byte 186 if indent { 187 b, err = xml.MarshalIndent(n.This(), "", " ") 188 } else { 189 b, err = xml.Marshal(n.This()) 190 } 191 if err != nil { 192 log.Println(err) 193 return err 194 } 195 _, err = writer.Write(b) 196 if err != nil { 197 log.Println(err) 198 return err 199 } 200 return nil 201 } 202 203 // ReadXML reads the tree from an XML-encoded byte string over io.Reader, calls 204 // UnmarshalPost to recover pointers from paths. 205 func (n *Node) ReadXML(reader io.Reader) error { 206 var err error 207 if err = ThisCheck(n); err != nil { 208 log.Println(err) 209 return err 210 } 211 b, err := ioutil.ReadAll(reader) 212 if err != nil { 213 log.Println(err) 214 return err 215 } 216 updt := n.UpdateStart() 217 err = xml.Unmarshal(b, n.This()) // key use of this! 218 UnmarshalPost(n.This()) 219 n.SetChildAdded() // this might not be set.. 220 n.UpdateEnd(updt) 221 return nil 222 } 223 224 // ParentAllChildren walks the tree down from current node and call 225 // SetParent on all children -- needed after an Unmarshal. 226 func ParentAllChildren(kn Ki) { 227 for _, child := range *kn.Children() { 228 if child != nil { 229 child.AsNode().Par = kn 230 ParentAllChildren(child) 231 } 232 } 233 } 234 235 // UnmarshalPost must be called after an Unmarshal -- calls 236 // ParentAllChildren. 237 func UnmarshalPost(kn Ki) { 238 ParentAllChildren(kn) 239 } 240 241 ////////////////////////////////////////////////////////////////////////// 242 // Slice 243 244 // MarshalJSON saves the length and type, name information for each object in a 245 // slice, as a separate struct-like record at the start, followed by the 246 // structs for each element in the slice -- this allows the Unmarshal to first 247 // create all the elements and then load them 248 func (sl Slice) MarshalJSON() ([]byte, error) { 249 nk := len(sl) 250 b := make([]byte, 0, nk*100+20) 251 if nk == 0 { 252 b = append(b, []byte("null")...) 253 return b, nil 254 } 255 nstr := fmt.Sprintf("[{\"n\":%d,", nk) 256 b = append(b, []byte(nstr)...) 257 for i, kid := range sl { 258 // fmt.Printf("json out of %v\n", kid.Path()) 259 knm := kit.Types.TypeName(reflect.TypeOf(kid).Elem()) 260 tstr := fmt.Sprintf("\"type\":\"%v\", \"name\": \"%v\"", knm, kid.Name()) // todo: escape names! 261 b = append(b, []byte(tstr)...) 262 if i < nk-1 { 263 b = append(b, []byte(",")...) 264 } 265 } 266 b = append(b, []byte("},")...) 267 for i, kid := range sl { 268 var err error 269 var kb []byte 270 kb, err = json.Marshal(kid) 271 if err == nil { 272 b = append(b, []byte("{")...) 273 b = append(b, kb[1:len(kb)-1]...) 274 b = append(b, []byte("}")...) 275 if i < nk-1 { 276 b = append(b, []byte(",")...) 277 } 278 } else { 279 fmt.Printf("error doing json.Marshall from kid: %v\n", kid.Path()) 280 log.Println(err) 281 fmt.Printf("output to point of error: %v\n", string(b)) 282 } 283 } 284 b = append(b, []byte("]")...) 285 // fmt.Printf("json out: %v\n", string(b)) 286 return b, nil 287 } 288 289 /////////////////////////////////////////////////////////////////////////// 290 // JSON 291 292 // UnmarshalJSON parses the length and type information for each object in the 293 // slice, creates the new slice with those elements, and then loads based on 294 // the remaining bytes which represent each element 295 func (sl *Slice) UnmarshalJSON(b []byte) error { 296 // fmt.Printf("json in: %v\n", string(b)) 297 if bytes.Equal(b, []byte("null")) { 298 *sl = nil 299 return nil 300 } 301 lb := bytes.IndexRune(b, '{') 302 rb := bytes.IndexRune(b, '}') 303 if lb < 0 || rb < 0 { // probably null 304 return nil 305 } 306 // todo: if name contains "," this won't work.. 307 flds := bytes.Split(b[lb+1:rb], []byte(",")) 308 if len(flds) == 0 { 309 return errors.New("Slice UnmarshalJSON: no child data found") 310 } 311 // fmt.Printf("flds[0]:\n%v\n", string(flds[0])) 312 ns := bytes.Index(flds[0], []byte("\"n\":")) 313 bn := bytes.TrimSpace(flds[0][ns+4:]) 314 315 n64, err := strconv.ParseInt(string(bn), 10, 64) 316 if err != nil { 317 return err 318 } 319 n := int(n64) 320 if n == 0 { 321 return nil 322 } 323 // fmt.Printf("n parsed: %d from %v\n", n, string(bn)) 324 325 tnl := make(kit.TypeAndNameList, n) 326 327 for i := 0; i < n; i++ { 328 fld := flds[2*i+1] 329 // fmt.Printf("fld:\n%v\n", string(fld)) 330 ti := bytes.Index(fld, []byte("\"type\":")) 331 tn := string(bytes.Trim(bytes.TrimSpace(fld[ti+7:]), "\"")) 332 fld = flds[2*i+2] 333 ni := bytes.Index(fld, []byte("\"name\":")) 334 nm := string(bytes.Trim(bytes.TrimSpace(fld[ni+7:]), "\"")) 335 // fmt.Printf("making type: %v", tn) 336 typ := kit.Types.Type(tn) 337 if typ == nil { 338 return fmt.Errorf("ki.Slice UnmarshalJSON: kit.Types type name not found: %v", tn) 339 } 340 tnl[i].Type = typ 341 tnl[i].Name = nm 342 } 343 344 sl.Config(nil, tnl) 345 346 nwk := make([]Ki, n) // allocate new slice containing *pointers* to kids 347 348 for i, kid := range *sl { 349 nwk[i] = kid 350 } 351 352 cb := make([]byte, 0, 1+len(b)-rb) 353 cb = append(cb, []byte("[")...) 354 cb = append(cb, b[rb+2:]...) 355 356 // fmt.Printf("loading:\n%v", string(cb)) 357 358 err = json.Unmarshal(cb, &nwk) 359 if err != nil { 360 return err 361 } 362 return nil 363 } 364 365 // todo: save N as an attr instead of a full element 366 367 // MarshalXML saves the length and type information for each object in a 368 // slice, as a separate struct-like record at the start, followed by the 369 // structs for each element in the slice -- this allows the Unmarshal to first 370 // create all the elements and then load them 371 func (sl Slice) MarshalXML(e *xml.Encoder, start xml.StartElement) error { 372 tokens := []xml.Token{start} 373 nk := len(sl) 374 nt := xml.StartElement{Name: xml.Name{Space: "", Local: "N"}} 375 tokens = append(tokens, nt, xml.CharData(fmt.Sprintf("%d", nk)), xml.EndElement{Name: nt.Name}) 376 for _, kid := range sl { 377 knm := kit.Types.TypeName(reflect.TypeOf(kid).Elem()) 378 t := xml.StartElement{Name: xml.Name{Space: "", Local: "Type"}} 379 tokens = append(tokens, t, xml.CharData(knm), xml.EndElement{Name: t.Name}) 380 } 381 for _, t := range tokens { 382 err := e.EncodeToken(t) 383 if err != nil { 384 return err 385 } 386 } 387 err := e.Flush() 388 if err != nil { 389 return err 390 } 391 for _, kid := range sl { 392 knm := reflect.TypeOf(kid).Elem().Name() 393 ct := xml.StartElement{Name: xml.Name{Space: "", Local: knm}} 394 err := e.EncodeElement(kid, ct) 395 if err != nil { 396 return err 397 } 398 } 399 err = e.EncodeToken(xml.EndElement{Name: start.Name}) 400 if err != nil { 401 return err 402 } 403 err = e.Flush() 404 if err != nil { 405 return err 406 } 407 return nil 408 } 409 410 // DecodeXMLStartEl reads a start element token 411 func DecodeXMLStartEl(d *xml.Decoder) (start xml.StartElement, err error) { 412 for { 413 var t xml.Token 414 t, err = d.Token() 415 if err != nil { 416 log.Printf("ki.DecodeXMLStartEl err %v\n", err) 417 return 418 } 419 switch tv := t.(type) { 420 case xml.StartElement: 421 start = tv 422 return 423 case xml.CharData: // actually passes the spaces and everything through here 424 continue 425 case xml.EndElement: 426 err = fmt.Errorf("ki.DecodeXMLStartEl: got unexpected EndElement") 427 log.Println(err) 428 return 429 default: 430 continue 431 } 432 } 433 } 434 435 // DecodeXMLEndEl reads an end element 436 func DecodeXMLEndEl(d *xml.Decoder, start xml.StartElement) error { 437 for { 438 t, err := d.Token() 439 if err != nil { 440 log.Printf("ki.DecodeXMLEndEl err %v\n", err) 441 return err 442 } 443 switch tv := t.(type) { 444 case xml.EndElement: 445 if tv.Name != start.Name { 446 err = fmt.Errorf("ki.DecodeXMLEndEl: EndElement: %v does not match StartElement: %v", tv.Name, start.Name) 447 log.Println(err) 448 return err 449 } 450 return nil 451 case xml.CharData: // actually passes the spaces and everything through here 452 continue 453 case xml.StartElement: 454 err = fmt.Errorf("ki.DecodeXMLEndEl: got unexpected StartElement: %v", tv.Name) 455 log.Println(err) 456 return err 457 default: 458 continue 459 } 460 } 461 } 462 463 // DecodeXMLCharData reads char data.. 464 func DecodeXMLCharData(d *xml.Decoder) (val string, err error) { 465 for { 466 var t xml.Token 467 t, err = d.Token() 468 if err != nil { 469 log.Printf("ki.DecodeXMLCharData err %v\n", err) 470 return 471 } 472 switch tv := t.(type) { 473 case xml.CharData: 474 val = string([]byte(tv)) 475 return 476 case xml.StartElement: 477 err = fmt.Errorf("ki.DecodeXMLCharData: got unexpected StartElement: %v", tv.Name) 478 log.Println(err) 479 return 480 case xml.EndElement: 481 err = fmt.Errorf("ki.DecodeXMLCharData: got unexpected EndElement: %v", tv.Name) 482 log.Println(err) 483 return 484 } 485 } 486 } 487 488 // DecodeXMLCharEl reads a start / chardata / end sequence of 3 elements, returning name, val 489 func DecodeXMLCharEl(d *xml.Decoder) (name, val string, err error) { 490 var st xml.StartElement 491 st, err = DecodeXMLStartEl(d) 492 if err != nil { 493 return 494 } 495 name = st.Name.Local 496 val, err = DecodeXMLCharData(d) 497 if err != nil { 498 return 499 } 500 err = DecodeXMLEndEl(d, st) 501 if err != nil { 502 return 503 } 504 return 505 } 506 507 // UnmarshalXML parses the length and type information for each object in the 508 // slice, creates the new slice with those elements, and then loads based on 509 // the remaining bytes which represent each element 510 func (sl *Slice) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { 511 // for _, attr := range start.Attr { 512 // // todo: need to set the props from name / value -- don't have parent though! 513 // } 514 name, val, err := DecodeXMLCharEl(d) 515 if err != nil { 516 return err 517 } 518 if name == "N" { 519 n64, err := strconv.ParseInt(string(val), 10, 64) 520 if err != nil { 521 return err 522 } 523 n := int(n64) 524 if n == 0 { 525 return DecodeXMLEndEl(d, start) 526 } 527 // fmt.Printf("n parsed: %d from %v\n", n, string(val)) 528 nwk := make([]Ki, 0, n) // allocate new slice 529 530 for i := 0; i < n; i++ { 531 name, val, err = DecodeXMLCharEl(d) 532 if name == "Type" { 533 tn := strings.TrimSpace(val) 534 // fmt.Printf("making type: %v\n", tn) 535 typ := kit.Types.Type(tn) 536 if typ == nil { 537 return fmt.Errorf("ki.Slice UnmarshalXML: kit.Types type name not found: %v", tn) 538 } 539 nkid := reflect.New(typ).Interface() 540 // fmt.Printf("nkid is new obj of type %T val: %+v\n", nkid, nkid) 541 kid, ok := nkid.(Ki) 542 if !ok { 543 return fmt.Errorf("ki.Slice UnmarshalXML: New child of type %v cannot convert to Ki", tn) 544 } 545 InitNode(kid) 546 nwk = append(nwk, kid) 547 } 548 } 549 550 for i := 0; i < n; i++ { 551 st, err := DecodeXMLStartEl(d) 552 if err != nil { 553 return err 554 } 555 // todo: could double-check st 556 err = d.DecodeElement(nwk[i], &st) 557 if err != nil { 558 log.Printf("%v", err) 559 return err 560 } 561 } 562 *sl = append(*sl, nwk...) 563 // } else { 564 } 565 // todo: in theory we could just parse a list of type names as tags, but for the "dump" format 566 // this is more robust. 567 return DecodeXMLEndEl(d, start) // final end 568 }