...

Source file src/github.com/clbanning/mxj/v2/examples/getmetrics1.go

Documentation: github.com/clbanning/mxj/v2/examples

     1  // getmetrics1.go - transform Eclipse Metrics (v3) XML report into CSV files for each metric
     2  // Uses NewMapXmlReader on os.File without copying the raw XML into a buffer while decoding..
     3  // Shows no significant overhead for not first buffering large XML file as in getmetrics2.go.
     4  
     5  /*
     6  I needed to convert a large (14.9 MB) XML data set from an Eclipse metrics report on an
     7  application that had 355,100 lines of code in 211 packages into CSV data sets.  The report
     8  included application-, package-, class- and method-level metrics reported in an element,
     9  "Value", with varying attributes.
    10  	<Value value=""/>
    11  	<Value name="" package="" value=""/>
    12  	<Value name="" source="" package="" value=""/>
    13  	<Value name="" source="" package="" value="" inrange=""/>
    14  
    15  In addition, the metrics were reported with two different "Metric" compound elements:
    16  	<Metrics>
    17  		<Metric id="" description="">
    18  			<Values>
    19  				<Value.../>
    20  				...
    21  			</Values>
    22  		</Metric>
    23  		...
    24  		<Metric id="" description="">
    25  			<Value.../>
    26  		</Metric>
    27  		...
    28  	</Metrics>
    29  
    30  To run this example, extract the metrics_data.xml file from metrics_data.zip, then:
    31  	> go run getmetrics1 -file=metrics_data.xml
    32  
    33  The output will be a set of "csv" files.
    34  */
    35  
    36  package main
    37  
    38  import (
    39  	"flag"
    40  	"fmt"
    41  	"github.com/clbanning/mxj"
    42  	"log"
    43  	"os"
    44  	"sort"
    45  	"time"
    46  )
    47  
    48  func main() {
    49  	var file string
    50  	flag.StringVar(&file, "file", "", "file to process")
    51  	flag.Parse()
    52  
    53  	fh, fherr := os.Open(file)
    54  	if fherr != nil {
    55  		fmt.Println("fherr:", fherr.Error())
    56  		return
    57  	}
    58  	defer fh.Close()
    59  	fmt.Println(time.Now().String(), "... File Opened:", file)
    60  
    61  	/*
    62  		// Get the XML data set from the file.
    63  		fs, _ := fh.Stat()
    64  		xmldata := make([]byte, fs.Size())
    65  		n, frerr := fh.Read(xmldata)
    66  		if frerr != nil {
    67  			fmt.Println("frerr:", frerr.Error())
    68  			return
    69  		}
    70  		if int64(n) != fs.Size() {
    71  			fmt.Println("n:", n, "fs.Size():", fs.Size())
    72  			return
    73  		}
    74  		fmt.Println(time.Now().String(), "... File Read - size:", fs.Size())
    75  
    76  		// load XML into a Map value
    77  		m, merr := mxj.NewMapXml(xmldata)
    78  	*/
    79  	// Consume the file using os.File Reader.
    80  	// Note: there is a single record with root tag of "Metrics".
    81  	m, merr := mxj.NewMapXmlReader(fh)
    82  	if merr != nil {
    83  		log.Fatal("merr:", merr.Error())
    84  	}
    85  	fmt.Println(time.Now().String(), "... XML Unmarshaled - len:", len(m))
    86  
    87  	// Get just the key values of interest.
    88  	// Could also use m.ValuesForKey("Metric"),
    89  	// since there's just the one path.
    90  	metricVals, err := m.ValuesForPath("Metrics.Metric")
    91  	if err != nil {
    92  		log.Fatal("err:", err.Error())
    93  	}
    94  	fmt.Println(time.Now().String(), "... ValuesFromKeyPath - len:", len(metricVals))
    95  
    96  	// now just manipulate Map entries returned as []interface{} array.
    97  	for _, v := range metricVals {
    98  		aMetricVal := v.(map[string]interface{})
    99  
   100  		// create file to hold csv data sets
   101  		id := aMetricVal["-id"].(string)
   102  		desc := aMetricVal["-description"].(string)
   103  		mf, mferr := os.OpenFile(id+".csv", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0666)
   104  		if mferr != nil {
   105  			fmt.Println("mferr:", mferr.Error())
   106  			return
   107  		}
   108  
   109  		fmt.Print(time.Now().String(), " id: ", id, " desc: ", desc)
   110  		mf.WriteString(id + "," + desc + "\n")
   111  
   112  		// rescan looking for keys with data: Values or Value
   113  		for key, val := range aMetricVal {
   114  			switch key {
   115  			case "Values":
   116  				// extract the list of "Value" from map
   117  				values := val.(map[string]interface{})["Value"].([]interface{})
   118  				fmt.Println(" len(Values):", len(values))
   119  
   120  				// first line in file is the metric label values (keys)
   121  				var gotKeys bool
   122  				for _, vval := range values {
   123  					valueEntry := vval.(map[string]interface{})
   124  
   125  					// no guarantee that range on map will follow any sequence
   126  					lv := len(valueEntry)
   127  					list := make([][2]string, lv)
   128  					var i int
   129  					for k, v := range valueEntry {
   130  						list[i][0] = k
   131  						list[i][1] = v.(string)
   132  						i++
   133  					}
   134  					sort.Sort(mylist(list))
   135  
   136  					// extract keys as column header on first pass
   137  					if !gotKeys {
   138  						// print out the keys
   139  						var gotFirstKey bool
   140  						// for kk, _ := range valueEntry {
   141  						for i := 0; i < lv; i++ {
   142  							if gotFirstKey {
   143  								mf.WriteString(",")
   144  							} else {
   145  								gotFirstKey = true
   146  							}
   147  							// strip prepended hyphen
   148  							mf.WriteString((list[i][0])[1:])
   149  						}
   150  						mf.WriteString("\n")
   151  						gotKeys = true
   152  					}
   153  
   154  					// print out values
   155  					var gotFirstVal bool
   156  					// for _, vv := range valueEntry {
   157  					for i := 0; i < lv; i++ {
   158  						if gotFirstVal {
   159  							mf.WriteString(",")
   160  						} else {
   161  							gotFirstVal = true
   162  						}
   163  						mf.WriteString(list[i][1])
   164  					}
   165  
   166  					// terminate row of data
   167  					mf.WriteString("\n")
   168  				}
   169  			case "Value":
   170  				vv := val.(map[string]interface{})
   171  				fmt.Println(" len(Value):", len(vv))
   172  				mf.WriteString("value\n" + vv["-value"].(string) + "\n")
   173  			}
   174  		}
   175  		mf.Close()
   176  	}
   177  }
   178  
   179  type mylist [][2]string
   180  
   181  func (m mylist) Len() int {
   182  	return len(m)
   183  }
   184  
   185  func (m mylist) Less(i, j int) bool {
   186  	if m[i][0] > m[j][0] {
   187  		return false
   188  	}
   189  	return true
   190  }
   191  
   192  func (m mylist) Swap(i, j int) {
   193  	m[i], m[j] = m[j], m[i]
   194  }
   195  

View as plain text