1 // mxj - A collection of map[string]interface{} and associated XML and JSON utilities.
2 // Copyright 2012-2014 Charles Banning. All rights reserved.
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file
5 6 package mxj
7 8 import (
9 "fmt"
10 "sort"
11 )
12 13 const (
14 Cast = true // for clarity - e.g., mxj.NewMapXml(doc, mxj.Cast)
15 SafeEncoding = true // ditto - e.g., mv.Json(mxj.SafeEncoding)
16 )
17 18 type Map map[string]interface{}
19 20 // Allocate a Map.
21 func New() Map {
22 m := make(map[string]interface{}, 0)
23 return m
24 }
25 26 // Cast a Map to map[string]interface{}
27 func (mv Map) Old() map[string]interface{} {
28 return mv
29 }
30 31 // Return a copy of mv as a newly allocated Map. If the Map only contains string,
32 // numeric, map[string]interface{}, and []interface{} values, then it can be thought
33 // of as a "deep copy." Copying a structure (or structure reference) value is subject
34 // to the noted restrictions.
35 // NOTE: If 'mv' includes structure values with, possibly, JSON encoding tags
36 // then only public fields of the structure are in the new Map - and with
37 // keys that conform to any encoding tag instructions. The structure itself will
38 // be represented as a map[string]interface{} value.
39 func (mv Map) Copy() (Map, error) {
40 // this is the poor-man's deep copy
41 // not efficient, but it works
42 j, jerr := mv.Json()
43 // must handle, we don't know how mv got built
44 if jerr != nil {
45 return nil, jerr
46 }
47 return NewMapJson(j)
48 }
49 50 // --------------- StringIndent ... from x2j.WriteMap -------------
51 52 // Pretty print a Map.
53 func (mv Map) StringIndent(offset ...int) string {
54 return writeMap(map[string]interface{}(mv), true, true, offset...)
55 }
56 57 // Pretty print a Map without the value type information - just key:value entries.
58 func (mv Map) StringIndentNoTypeInfo(offset ...int) string {
59 return writeMap(map[string]interface{}(mv), false, true, offset...)
60 }
61 62 // writeMap - dumps the map[string]interface{} for examination.
63 // 'typeInfo' causes value type to be printed.
64 // 'offset' is initial indentation count; typically: Write(m).
65 func writeMap(m interface{}, typeInfo, root bool, offset ...int) string {
66 var indent int
67 if len(offset) == 1 {
68 indent = offset[0]
69 }
70 71 var s string
72 switch m.(type) {
73 case []interface{}:
74 if typeInfo {
75 s += "[[]interface{}]"
76 }
77 for _, v := range m.([]interface{}) {
78 s += "\n"
79 for i := 0; i < indent; i++ {
80 s += " "
81 }
82 s += writeMap(v, typeInfo, false, indent+1)
83 }
84 case map[string]interface{}:
85 list := make([][2]string, len(m.(map[string]interface{})))
86 var n int
87 for k, v := range m.(map[string]interface{}) {
88 list[n][0] = k
89 list[n][1] = writeMap(v, typeInfo, false, indent+1)
90 n++
91 }
92 sort.Sort(mapList(list))
93 for _, v := range list {
94 if root {
95 root = false
96 } else {
97 s += "\n"
98 }
99 for i := 0; i < indent; i++ {
100 s += " "
101 }
102 s += v[0] + " : " + v[1]
103 }
104 default:
105 if typeInfo {
106 s += fmt.Sprintf("[%T] %+v", m, m)
107 } else {
108 s += fmt.Sprintf("%+v", m)
109 }
110 }
111 return s
112 }
113 114 // ======================== utility ===============
115 116 type mapList [][2]string
117 118 func (ml mapList) Len() int {
119 return len(ml)
120 }
121 122 func (ml mapList) Swap(i, j int) {
123 ml[i], ml[j] = ml[j], ml[i]
124 }
125 126 func (ml mapList) Less(i, j int) bool {
127 return ml[i][0] <= ml[j][0]
128 }
129