github.com/boki/go-xmp@v1.0.1/xmp/json.go (about) 1 // Copyright (c) 2017-2018 Alexander Eichhorn 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 // not use this file except in compliance with the License. You may obtain 5 // a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations 13 // under the License. 14 15 package xmp 16 17 import ( 18 "encoding/json" 19 "encoding/xml" 20 "fmt" 21 "reflect" 22 "strconv" 23 ) 24 25 type jsonDocument struct { 26 About string `json:"about,omitempty"` 27 Toolkit string `json:"toolkit,omitempty"` 28 Namespaces map[string]string `json:"namespaces"` 29 Models map[string]json.RawMessage `json:"models"` 30 } 31 32 type jsonOutDocument struct { 33 About string `json:"about,omitempty"` 34 Toolkit string `json:"toolkit,omitempty"` 35 Namespaces map[string]string `json:"namespaces"` 36 Models map[string]interface{} `json:"models"` 37 } 38 39 func (d *Document) MarshalJSON() ([]byte, error) { 40 // sync individual models to establish correct XMP entries 41 if err := d.syncToXMP(); err != nil { 42 return nil, err 43 } 44 45 out := &jsonOutDocument{ 46 About: d.about, 47 Toolkit: d.toolkit, 48 Namespaces: make(map[string]string), 49 Models: make(map[string]interface{}), 50 } 51 52 if out.Toolkit == "" { 53 out.Toolkit = XMP_TOOLKIT_VERSION 54 } 55 56 // We're using the regular XMP decoder with a JSON boilerplate. 57 e := NewEncoder(nil) 58 e.intNsMap = d.intNsMap 59 e.extNsMap = d.extNsMap 60 defer e.root.Close() 61 62 // 1 build output node tree (model -> nodes+attr with one root node per 63 // XMP namespace) 64 for _, n := range d.nodes { 65 // 1.1 encode the model (Note: models typically use multiple XMP namespaces) 66 // so we generate wrapper nodes on the fly 67 if n.Model != nil { 68 if err := e.marshalValue(reflect.ValueOf(n.Model), nil, e.root, true); err != nil { 69 return nil, err 70 } 71 } 72 73 // 1.2 merge external nodes (Note: all ext nodes collected under a 74 // document node belong to the same namespace) 75 ns := e.findNs(n.XMLName) 76 if ns == nil { 77 return nil, fmt.Errorf("xmp: missing namespace for model node %s\n", n.XMLName.Local) 78 } 79 node := e.root.Nodes.FindNode(ns) 80 if node == nil { 81 node = NewNode(n.XMLName) 82 e.root.AddNode(node) 83 } 84 node.Nodes = append(node.Nodes, copyNodes(n.Nodes)...) 85 86 // 1.3 merge external attributes (Note: all ext attr collected under a 87 // document node belong to the same namespace) 88 node.Attr = append(node.Attr, n.Attr...) 89 } 90 91 // 2 collect root-node namespaces 92 for _, n := range e.root.Nodes { 93 for _, v := range n.Namespaces(d) { 94 if v == nsX || v == nsXML || v == nsRDF { 95 continue 96 } 97 out.Namespaces[v.GetName()] = v.GetURI() 98 } 99 } 100 101 // 3 convert node tree to json, ignore empty root nodes 102 for _, n := range e.root.Nodes { 103 if n.IsZero() { 104 continue 105 } 106 if m, err := nodeToJson(n); err != nil { 107 return nil, err 108 } else { 109 // add model under it's namespace name 110 out.Models[n.Namespace()] = m 111 } 112 } 113 114 b, err := json.Marshal(out) 115 if err != nil { 116 return nil, err 117 } 118 119 // 4 output as json 120 return b, nil 121 } 122 123 func nodeToJson(n *Node) (interface{}, error) { 124 // process value leaf nodes (Note: value and model are mutually exclusive) 125 if n.Value != "" { 126 return &n.Value, nil 127 } 128 129 out := make(map[string]interface{}) 130 131 // add node attributes 132 for _, v := range n.Attr { 133 name := v.Name.Local 134 // Note: stripping rdf attributes from external child nodes 135 // that are relinked during marshal with all attributes 136 if name == "rdf:parseType" { 137 continue 138 } 139 out[name] = v.Value 140 } 141 142 // add node children 143 for _, v := range n.Nodes { 144 switch v.FullName() { 145 case "rdf:Bag", "rdf:Seq": 146 // skip outer node and unwrap inner li nodes as array elements 147 a := make([]interface{}, 0) 148 for _, vv := range v.Nodes { 149 if vv.Value != "" { 150 // simple lists (i.e. string lists or numbers etc) 151 a = append(a, &vv.Value) 152 } else { 153 // object lists 154 // m := make(map[string]interface{}) 155 if m, err := nodeToJson(vv); err != nil { 156 return nil, err 157 } else { 158 a = append(a, m) 159 } 160 } 161 } 162 return a, nil 163 164 case "rdf:Alt": 165 // determine content type (strings or objects) 166 if len(v.Nodes) > 0 && len(v.Nodes[0].Nodes) > 0 { 167 // object type: skip outer node and insert interface 168 a := make([]interface{}, 0) 169 for _, vv := range v.Nodes { 170 if m, err := nodeToJson(vv); err != nil { 171 return nil, err 172 } else { 173 a = append(a, m) 174 } 175 } 176 return a, nil 177 } else { 178 // string type: skip outer node and insert ArrayItems 179 var defLangContent string 180 var defLangIndex int = -1 181 a := make([]*AltItem, 0) 182 for i, vv := range v.Nodes { 183 // string arrays 184 ai := &AltItem{ 185 Value: vv.Value, 186 } 187 langAttr := vv.GetAttr("", "lang") 188 if len(langAttr) > 0 { 189 ai.Lang = langAttr[0].Value 190 } 191 if ai.Lang == "x-default" { 192 ai.IsDefault = true 193 defLangContent = ai.Value 194 defLangIndex = i 195 } 196 a = append(a, ai) 197 } 198 // mark the default item 199 if defLangContent != "" && len(a) > 1 { 200 for i, l := 0, len(a); i < l; i++ { 201 if a[i].Value == defLangContent { 202 a[i].IsDefault = true 203 } 204 } 205 // remove the duplicate default element 206 if defLangIndex > -1 { 207 a = append(a[:defLangIndex], a[defLangIndex+1:]...) 208 } 209 } 210 return a, nil 211 } 212 213 default: 214 if m, err := nodeToJson(v); err != nil { 215 return nil, err 216 } else { 217 out[v.FullName()] = m 218 } 219 } 220 } 221 222 return out, nil 223 } 224 225 // The difference to regular JSON decoding is that we must take care of 226 // unknown properties that Go's JSON decoder simply ignores. In our XMP 227 // model we capture everything unknown in child nodes. Note: due to internal 228 // namespace lookups we cannot use node attributes, because attribute unmarshal 229 // will fail for all unknown namespaces. 230 func (d *Document) UnmarshalJSON(data []byte) error { 231 in := &jsonDocument{ 232 Namespaces: make(map[string]string), 233 Models: make(map[string]json.RawMessage), 234 } 235 236 if err := json.Unmarshal(data, in); err != nil { 237 return fmt.Errorf("xmp: json unmarshal failed: %v", err) 238 } 239 240 // We're using the regular XMP decoder with a JSON boilerplate. 241 dec := NewDecoder(nil) 242 243 // register namespaces 244 dec.about = in.About 245 dec.toolkit = in.Toolkit 246 for prefix, uri := range in.Namespaces { 247 dec.addNamespace(prefix, uri) 248 } 249 250 // build node tree from JSON models 251 root := NewNode(emptyName) 252 defer root.Close() 253 for name, b := range in.Models { 254 node := NewNode(xml.Name{Local: name}) 255 root.Nodes = append(root.Nodes, node) 256 content := make(map[string]interface{}) 257 if err := json.Unmarshal(b, &content); err != nil { 258 return fmt.Errorf("xmp: json unmarshal model '%s' failed: %v", name, err) 259 } 260 for n, v := range content { 261 jsonToNode(n, v, node) 262 } 263 } 264 265 // run node tree through xmp unmarshaler 266 for _, n := range root.Nodes { 267 // process attributes 268 for _, v := range n.Attr { 269 if err := dec.decodeAttribute(&dec.nodes, v); err != nil { 270 return err 271 } 272 } 273 // process child nodes 274 for _, v := range n.Nodes { 275 if err := dec.decodeNode(&dec.nodes, v); err != nil { 276 return err 277 } 278 } 279 } 280 281 // copy decoded values to document 282 d.toolkit = dec.toolkit 283 d.about = dec.about 284 d.nodes = dec.nodes 285 d.intNsMap = dec.intNsMap 286 d.extNsMap = dec.extNsMap 287 return d.syncFromXMP() 288 } 289 290 func jsonToNode(name string, v interface{}, node *Node) { 291 switch { 292 case isSimpleValue(v): 293 var s string 294 switch val := v.(type) { 295 case string: 296 s = val 297 case float64: 298 s = strconv.FormatFloat(val, 'f', -1, 64) 299 case bool: 300 s = strconv.FormatBool(val) 301 case nil: 302 return 303 } 304 if name != "" && name != "rdf:value" { 305 // add simple values as child nodes with string value 306 attrNode := NewNode(xml.Name{Local: name}) 307 attrNode.Value = s 308 node.Nodes = append(node.Nodes, attrNode) 309 } else { 310 // add as node value when no name is given (i.e. used in string arrays) 311 node.Value = s 312 } 313 314 case isArrayValue(v): 315 // arrays of arrays are not supported in XMP 316 if name == "" { 317 return 318 } 319 320 // add arrays as Seq/li or Alt/li child nodes 321 containerNode := NewNode(xml.Name{Local: name}) 322 node.Nodes = append(node.Nodes, containerNode) 323 typ := getArrayType(v) 324 anode := NewNode(xml.Name{Space: nsRDF.GetURI(), Local: string(typ)}) 325 containerNode.Nodes = append(containerNode.Nodes, anode) 326 327 for _, av := range v.([]interface{}) { 328 linode := NewNode(xml.Name{Space: nsRDF.GetURI(), Local: "li"}) 329 anode.Nodes = append(anode.Nodes, linode) 330 if typ == ArrayTypeAlternative { 331 item := av.(map[string]interface{}) 332 def := item["isDefault"].(bool) 333 lang := item["lang"].(string) 334 val := item["value"].(string) 335 336 // add single node when default || !default && lang != "" 337 if def || !def && lang != "" { 338 if def { 339 linode.Attr = append(linode.Attr, Attr{ 340 Name: xml.Name{Local: "xml:lang"}, 341 Value: "x-default", 342 }) 343 linode.Value = val 344 } else { 345 linode.Attr = append(linode.Attr, Attr{ 346 Name: xml.Name{Local: "xml:lang"}, 347 Value: lang, 348 }) 349 linode.Value = val 350 } 351 } 352 353 // add second node for default && lang != "" 354 if def && lang != "" { 355 linode = NewNode(xml.Name{Space: nsRDF.GetURI(), Local: "li"}) 356 anode.Nodes = append(anode.Nodes, linode) 357 linode.Attr = append(linode.Attr, Attr{ 358 Name: xml.Name{Local: "xml:lang"}, 359 Value: lang, 360 }) 361 linode.Value = val 362 } 363 } else { 364 jsonToNode("", av, linode) 365 } 366 } 367 368 case isObjectValue(v): 369 // add objects as child nodes (recursive) 370 onode := node 371 if name != "" { 372 onode = NewNode(xml.Name{Local: name}) 373 node.Nodes = append(node.Nodes, onode) 374 } 375 for on, ov := range v.(map[string]interface{}) { 376 if isArrayItemType(v) && on == "value" { 377 jsonToNode("", ov, onode) 378 } else { 379 jsonToNode(on, ov, onode) 380 } 381 } 382 } 383 } 384 385 // JSON unmarshal helpers 386 func isSimpleValue(v interface{}) bool { 387 switch v.(type) { 388 case string, float64, bool, nil: 389 return true 390 default: 391 return false 392 } 393 } 394 395 func isObjectValue(v interface{}) bool { 396 switch v.(type) { 397 case map[string]interface{}: 398 return true 399 default: 400 return false 401 } 402 } 403 404 func isArrayValue(v interface{}) bool { 405 switch v.(type) { 406 case []interface{}: 407 return true 408 default: 409 return false 410 } 411 } 412 413 func isArrayItemType(v interface{}) bool { 414 // alternative array item 415 if isObjectValue(v) { 416 val := v.(map[string]interface{}) 417 if len(val) > 3 { 418 return false 419 } 420 if _, ok := val["value"]; !ok { 421 return false 422 } 423 if _, ok := val["lang"]; !ok { 424 return false 425 } 426 if _, ok := val["isDefault"]; !ok { 427 return false 428 } 429 return true 430 } 431 return false 432 } 433 434 func getArrayType(v interface{}) ArrayType { 435 slice, ok := v.([]interface{}) 436 if !ok || len(slice) == 0 { 437 return ArrayTypeOrdered 438 } 439 if isArrayItemType(slice[0]) { 440 return ArrayTypeAlternative 441 } 442 return ArrayTypeOrdered 443 }