mxj.go raw

   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