...

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

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

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

View as plain text