Просмотр исходного кода

Move asset encode/decode into its own file

Kaelan Patel 4 лет назад
Родитель
Сommit
37b3c2f4b7
2 измененных файлов с 913 добавлено и 880 удалено
  1. 0 880
      pkg/kubecost/asset.go
  2. 913 0
      pkg/kubecost/asset_unmarshal.go

+ 0 - 880
pkg/kubecost/asset.go

@@ -1,16 +1,12 @@
 package kubecost
 
 import (
-	"bytes"
 	"encoding"
 	"fmt"
-	"reflect"
 	"strings"
 	"sync"
 	"time"
 
-	gojson "encoding/json" // gojson is default golang json, required for RawMessage decoding
-
 	"github.com/kubecost/cost-model/pkg/log"
 	"github.com/kubecost/cost-model/pkg/util/json"
 )
@@ -603,82 +599,6 @@ func (a *Any) Equal(that Asset) bool {
 	return true
 }
 
-// MarshalJSON implements json.Marshaler
-func (a *Any) MarshalJSON() ([]byte, error) {
-	buffer := bytes.NewBufferString("{")
-	jsonEncode(buffer, "properties", a.Properties(), ",")
-	jsonEncode(buffer, "labels", a.Labels(), ",")
-	jsonEncode(buffer, "window", a.Window(), ",")
-	jsonEncodeString(buffer, "start", a.Start().Format(time.RFC3339), ",")
-	jsonEncodeString(buffer, "end", a.End().Format(time.RFC3339), ",")
-	jsonEncodeFloat64(buffer, "minutes", a.Minutes(), ",")
-	jsonEncodeFloat64(buffer, "adjustment", a.Adjustment(), ",")
-	jsonEncodeFloat64(buffer, "totalCost", a.TotalCost(), "")
-	buffer.WriteString("}")
-	return buffer.Bytes(), nil
-}
-
-func (a *Any) UnmarshalJSON(b []byte) error {
-
-	var f interface{}
-
-	err := json.Unmarshal(b, &f)
-	if err != nil {
-		return err
-	}
-
-	err = a.InterfaceToAny(f)
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// Converts interface{} to Any, carrying over relevant fields
-func (a *Any) InterfaceToAny(itf interface{}) error {
-
-	fmap := itf.(map[string]interface{})
-
-	// parse properties map to AssetProperties
-	fproperties := fmap["properties"].(map[string]interface{})
-	properties := toAssetProp(fproperties)
-
-	// parse labels map to AssetLabels
-	labels := make(map[string]string)
-	for k, v := range fmap["labels"].(map[string]interface{}) {
-		labels[k] = v.(string)
-	}
-
-	// parse start and end strings to time.Time
-	start, err := time.Parse(time.RFC3339, fmap["start"].(string))
-	if err != nil {
-		return err
-	}
-	end, err := time.Parse(time.RFC3339, fmap["end"].(string))
-	if err != nil {
-		return err
-	}
-
-	a.properties = &properties
-	a.labels = labels
-	a.start = start
-	a.end = end
-	a.window = Window{
-		start: &start,
-		end:   &end,
-	}
-
-	if adjustment, err := getTypedVal(fmap["adjustment"]); err == nil {
-		a.adjustment = adjustment.(float64)
-	}
-	if Cost, err := getTypedVal(fmap["totalCost"]); err == nil {
-		a.Cost = Cost.(float64) - a.adjustment
-	}
-
-	return nil
-}
-
 // String implements fmt.Stringer
 func (a *Any) String() string {
 	return toString(a)
@@ -906,87 +826,6 @@ func (ca *Cloud) Equal(a Asset) bool {
 	return true
 }
 
-// MarshalJSON implements json.Marshaler
-func (ca *Cloud) MarshalJSON() ([]byte, error) {
-	buffer := bytes.NewBufferString("{")
-	jsonEncodeString(buffer, "type", ca.Type().String(), ",")
-	jsonEncode(buffer, "properties", ca.Properties(), ",")
-	jsonEncode(buffer, "labels", ca.Labels(), ",")
-	jsonEncode(buffer, "window", ca.Window(), ",")
-	jsonEncodeString(buffer, "start", ca.Start().Format(time.RFC3339), ",")
-	jsonEncodeString(buffer, "end", ca.End().Format(time.RFC3339), ",")
-	jsonEncodeFloat64(buffer, "minutes", ca.Minutes(), ",")
-	jsonEncodeFloat64(buffer, "adjustment", ca.Adjustment(), ",")
-	jsonEncodeFloat64(buffer, "credit", ca.Credit, ",")
-	jsonEncodeFloat64(buffer, "totalCost", ca.TotalCost(), "")
-	buffer.WriteString("}")
-	return buffer.Bytes(), nil
-}
-
-func (ca *Cloud) UnmarshalJSON(b []byte) error {
-
-	var f interface{}
-
-	err := json.Unmarshal(b, &f)
-	if err != nil {
-		return err
-	}
-
-	err = ca.InterfaceToCloud(f)
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// Converts interface{} to Cloud, carrying over relevant fields
-func (ca *Cloud) InterfaceToCloud(itf interface{}) error {
-
-	fmap := itf.(map[string]interface{})
-
-	// parse properties map to AssetProperties
-	fproperties := fmap["properties"].(map[string]interface{})
-	properties := toAssetProp(fproperties)
-
-	// parse labels map to AssetLabels
-	labels := make(map[string]string)
-	for k, v := range fmap["labels"].(map[string]interface{}) {
-		labels[k] = v.(string)
-	}
-
-	// parse start and end strings to time.Time
-	start, err := time.Parse(time.RFC3339, fmap["start"].(string))
-	if err != nil {
-		return err
-	}
-	end, err := time.Parse(time.RFC3339, fmap["end"].(string))
-	if err != nil {
-		return err
-	}
-
-	ca.properties = &properties
-	ca.labels = labels
-	ca.start = start
-	ca.end = end
-	ca.window = Window{
-		start: &start,
-		end:   &end,
-	}
-
-	if adjustment, err := getTypedVal(fmap["adjustment"]); err == nil {
-		ca.adjustment = adjustment.(float64)
-	}
-	if Credit, err := getTypedVal(fmap["credit"]); err == nil {
-		ca.Credit = Credit.(float64)
-	}
-	if Cost, err := getTypedVal(fmap["totalCost"]); err == nil {
-		ca.Cost = Cost.(float64) - ca.adjustment - ca.Credit
-	}
-
-	return nil
-}
-
 // String implements fmt.Stringer
 func (ca *Cloud) String() string {
 	return toString(ca)
@@ -1170,77 +1009,6 @@ func (cm *ClusterManagement) Equal(a Asset) bool {
 	return true
 }
 
-// MarshalJSON implements json.Marshler
-func (cm *ClusterManagement) MarshalJSON() ([]byte, error) {
-	buffer := bytes.NewBufferString("{")
-	jsonEncodeString(buffer, "type", cm.Type().String(), ",")
-	jsonEncode(buffer, "properties", cm.Properties(), ",")
-	jsonEncode(buffer, "labels", cm.Labels(), ",")
-	jsonEncode(buffer, "window", cm.Window(), ",")
-	jsonEncodeString(buffer, "start", cm.Start().Format(time.RFC3339), ",")
-	jsonEncodeString(buffer, "end", cm.End().Format(time.RFC3339), ",")
-	jsonEncodeFloat64(buffer, "minutes", cm.Minutes(), ",")
-	jsonEncodeFloat64(buffer, "totalCost", cm.TotalCost(), "")
-	buffer.WriteString("}")
-	return buffer.Bytes(), nil
-}
-
-func (cm *ClusterManagement) UnmarshalJSON(b []byte) error {
-
-	var f interface{}
-
-	err := json.Unmarshal(b, &f)
-	if err != nil {
-		return err
-	}
-
-	err = cm.InterfaceToClusterManagement(f)
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// Converts interface{} to ClusterManagement, carrying over relevant fields
-func (cm *ClusterManagement) InterfaceToClusterManagement(itf interface{}) error {
-
-	fmap := itf.(map[string]interface{})
-
-	// parse properties map to AssetProperties
-	fproperties := fmap["properties"].(map[string]interface{})
-	properties := toAssetProp(fproperties)
-
-	// parse labels map to AssetLabels
-	labels := make(map[string]string)
-	for k, v := range fmap["labels"].(map[string]interface{}) {
-		labels[k] = v.(string)
-	}
-
-	// parse start and end strings to time.Time
-	start, err := time.Parse(time.RFC3339, fmap["start"].(string))
-	if err != nil {
-		return err
-	}
-	end, err := time.Parse(time.RFC3339, fmap["end"].(string))
-	if err != nil {
-		return err
-	}
-
-	cm.properties = &properties
-	cm.labels = labels
-	cm.window = Window{
-		start: &start,
-		end:   &end,
-	}
-
-	if Cost, err := getTypedVal(fmap["totalCost"]); err == nil {
-		cm.Cost = Cost.(float64)
-	}
-
-	return nil
-}
-
 // String implements fmt.Stringer
 func (cm *ClusterManagement) String() string {
 	return toString(cm)
@@ -1507,100 +1275,6 @@ func (d *Disk) Equal(a Asset) bool {
 	return true
 }
 
-// MarshalJSON implements the json.Marshaler interface
-func (d *Disk) MarshalJSON() ([]byte, error) {
-	buffer := bytes.NewBufferString("{")
-	jsonEncodeString(buffer, "type", d.Type().String(), ",")
-	jsonEncode(buffer, "properties", d.Properties(), ",")
-	jsonEncode(buffer, "labels", d.Labels(), ",")
-	jsonEncode(buffer, "window", d.Window(), ",")
-	jsonEncodeString(buffer, "start", d.Start().Format(time.RFC3339), ",")
-	jsonEncodeString(buffer, "end", d.End().Format(time.RFC3339), ",")
-	jsonEncodeFloat64(buffer, "minutes", d.Minutes(), ",")
-	jsonEncodeFloat64(buffer, "byteHours", d.ByteHours, ",")
-	jsonEncodeFloat64(buffer, "bytes", d.Bytes(), ",")
-	jsonEncode(buffer, "breakdown", d.Breakdown, ",")
-	jsonEncodeFloat64(buffer, "adjustment", d.Adjustment(), ",")
-	jsonEncodeFloat64(buffer, "totalCost", d.TotalCost(), "")
-	buffer.WriteString("}")
-	return buffer.Bytes(), nil
-}
-
-func (d *Disk) UnmarshalJSON(b []byte) error {
-
-	var f interface{}
-
-	err := json.Unmarshal(b, &f)
-	if err != nil {
-		return err
-	}
-
-	err = d.InterfaceToDisk(f)
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// Converts interface{} to Disk, carrying over relevant fields
-func (d *Disk) InterfaceToDisk(itf interface{}) error {
-
-	fmap := itf.(map[string]interface{})
-
-	// parse properties map to AssetProperties
-	fproperties := fmap["properties"].(map[string]interface{})
-	properties := toAssetProp(fproperties)
-
-	// parse labels map to AssetLabels
-	labels := make(map[string]string)
-	for k, v := range fmap["labels"].(map[string]interface{}) {
-		labels[k] = v.(string)
-	}
-
-	// parse start and end strings to time.Time
-	start, err := time.Parse(time.RFC3339, fmap["start"].(string))
-	if err != nil {
-		return err
-	}
-	end, err := time.Parse(time.RFC3339, fmap["end"].(string))
-	if err != nil {
-		return err
-	}
-
-	fbreakdown := fmap["breakdown"].(map[string]interface{})
-
-	breakdown := toBreakdown(fbreakdown)
-
-	d.properties = &properties
-	d.labels = labels
-	d.start = start
-	d.end = end
-	d.window = Window{
-		start: &start,
-		end:   &end,
-	}
-	d.Breakdown = &breakdown
-
-	if adjustment, err := getTypedVal(fmap["adjustment"]); err == nil {
-		d.adjustment = adjustment.(float64)
-	}
-	if Cost, err := getTypedVal(fmap["totalCost"]); err == nil {
-		d.Cost = Cost.(float64) - d.adjustment
-	}
-	if ByteHours, err := getTypedVal(fmap["byteHours"]); err == nil {
-		d.ByteHours = ByteHours.(float64)
-	}
-
-	// d.Local is not marhsaled, and cannot be calculated from marshaled values.
-	// Currently, it is just ignored and not set in the resulting unmarshal to Disk
-	//  be aware that this means a resulting Disk from an unmarshal is therefore NOT
-	// equal to the originally marshaled Disk.
-
-	return nil
-
-}
-
 // String implements fmt.Stringer
 func (d *Disk) String() string {
 	return toString(d)
@@ -1899,84 +1573,6 @@ func (n *Network) Equal(a Asset) bool {
 	return true
 }
 
-// MarshalJSON implements json.Marshal interface
-func (n *Network) MarshalJSON() ([]byte, error) {
-	buffer := bytes.NewBufferString("{")
-	jsonEncodeString(buffer, "type", n.Type().String(), ",")
-	jsonEncode(buffer, "properties", n.Properties(), ",")
-	jsonEncode(buffer, "labels", n.Labels(), ",")
-	jsonEncode(buffer, "window", n.Window(), ",")
-	jsonEncodeString(buffer, "start", n.Start().Format(time.RFC3339), ",")
-	jsonEncodeString(buffer, "end", n.End().Format(time.RFC3339), ",")
-	jsonEncodeFloat64(buffer, "minutes", n.Minutes(), ",")
-	jsonEncodeFloat64(buffer, "adjustment", n.Adjustment(), ",")
-	jsonEncodeFloat64(buffer, "totalCost", n.TotalCost(), "")
-	buffer.WriteString("}")
-	return buffer.Bytes(), nil
-}
-
-func (n *Network) UnmarshalJSON(b []byte) error {
-
-	var f interface{}
-
-	err := json.Unmarshal(b, &f)
-	if err != nil {
-		return err
-	}
-
-	err = n.InterfaceToNetwork(f)
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// Converts interface{} to Network, carrying over relevant fields
-func (n *Network) InterfaceToNetwork(itf interface{}) error {
-
-	fmap := itf.(map[string]interface{})
-
-	// parse properties map to AssetProperties
-	fproperties := fmap["properties"].(map[string]interface{})
-	properties := toAssetProp(fproperties)
-
-	// parse labels map to AssetLabels
-	labels := make(map[string]string)
-	for k, v := range fmap["labels"].(map[string]interface{}) {
-		labels[k] = v.(string)
-	}
-
-	// parse start and end strings to time.Time
-	start, err := time.Parse(time.RFC3339, fmap["start"].(string))
-	if err != nil {
-		return err
-	}
-	end, err := time.Parse(time.RFC3339, fmap["end"].(string))
-	if err != nil {
-		return err
-	}
-
-	n.properties = &properties
-	n.labels = labels
-	n.start = start
-	n.end = end
-	n.window = Window{
-		start: &start,
-		end:   &end,
-	}
-
-	if adjustment, err := getTypedVal(fmap["adjustment"]); err == nil {
-		n.adjustment = adjustment.(float64)
-	}
-	if Cost, err := getTypedVal(fmap["totalCost"]); err == nil {
-		n.Cost = Cost.(float64) - n.adjustment
-	}
-
-	return nil
-
-}
-
 // String implements fmt.Stringer
 func (n *Network) String() string {
 	return toString(n)
@@ -2321,132 +1917,6 @@ func (n *Node) Equal(a Asset) bool {
 	return true
 }
 
-// MarshalJSON implements json.Marshal interface
-func (n *Node) MarshalJSON() ([]byte, error) {
-	buffer := bytes.NewBufferString("{")
-	jsonEncodeString(buffer, "type", n.Type().String(), ",")
-	jsonEncode(buffer, "properties", n.Properties(), ",")
-	jsonEncode(buffer, "labels", n.Labels(), ",")
-	jsonEncode(buffer, "window", n.Window(), ",")
-	jsonEncodeString(buffer, "start", n.Start().Format(time.RFC3339), ",")
-	jsonEncodeString(buffer, "end", n.End().Format(time.RFC3339), ",")
-	jsonEncodeFloat64(buffer, "minutes", n.Minutes(), ",")
-	jsonEncodeString(buffer, "nodeType", n.NodeType, ",")
-	jsonEncodeFloat64(buffer, "cpuCores", n.CPUCores(), ",")
-	jsonEncodeFloat64(buffer, "ramBytes", n.RAMBytes(), ",")
-	jsonEncodeFloat64(buffer, "cpuCoreHours", n.CPUCoreHours, ",")
-	jsonEncodeFloat64(buffer, "ramByteHours", n.RAMByteHours, ",")
-	jsonEncodeFloat64(buffer, "GPUHours", n.GPUHours, ",")
-	jsonEncode(buffer, "cpuBreakdown", n.CPUBreakdown, ",")
-	jsonEncode(buffer, "ramBreakdown", n.RAMBreakdown, ",")
-	jsonEncodeFloat64(buffer, "preemptible", n.Preemptible, ",")
-	jsonEncodeFloat64(buffer, "discount", n.Discount, ",")
-	jsonEncodeFloat64(buffer, "cpuCost", n.CPUCost, ",")
-	jsonEncodeFloat64(buffer, "gpuCost", n.GPUCost, ",")
-	jsonEncodeFloat64(buffer, "gpuCount", n.GPUs(), ",")
-	jsonEncodeFloat64(buffer, "ramCost", n.RAMCost, ",")
-	jsonEncodeFloat64(buffer, "adjustment", n.Adjustment(), ",")
-	jsonEncodeFloat64(buffer, "totalCost", n.TotalCost(), "")
-	buffer.WriteString("}")
-	return buffer.Bytes(), nil
-}
-
-func (n *Node) UnmarshalJSON(b []byte) error {
-
-	var f interface{}
-
-	err := json.Unmarshal(b, &f)
-	if err != nil {
-		return err
-	}
-
-	err = n.InterfaceToNode(f)
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// Converts interface{} to Node, carrying over relevant fields
-func (n *Node) InterfaceToNode(itf interface{}) error {
-
-	fmap := itf.(map[string]interface{})
-
-	// parse properties map to AssetProperties
-	fproperties := fmap["properties"].(map[string]interface{})
-	properties := toAssetProp(fproperties)
-
-	// parse labels map to AssetLabels
-	labels := make(map[string]string)
-	for k, v := range fmap["labels"].(map[string]interface{}) {
-		labels[k] = v.(string)
-	}
-
-	// parse start and end strings to time.Time
-	start, err := time.Parse(time.RFC3339, fmap["start"].(string))
-	if err != nil {
-		return err
-	}
-	end, err := time.Parse(time.RFC3339, fmap["end"].(string))
-	if err != nil {
-		return err
-	}
-
-	fcpuBreakdown := fmap["cpuBreakdown"].(map[string]interface{})
-	framBreakdown := fmap["ramBreakdown"].(map[string]interface{})
-
-	cpuBreakdown := toBreakdown(fcpuBreakdown)
-	ramBreakdown := toBreakdown(framBreakdown)
-
-	n.properties = &properties
-	n.labels = labels
-	n.start = start
-	n.end = end
-	n.window = Window{
-		start: &start,
-		end:   &end,
-	}
-	n.CPUBreakdown = &cpuBreakdown
-	n.RAMBreakdown = &ramBreakdown
-
-	if adjustment, err := getTypedVal(fmap["adjustment"]); err == nil {
-		n.adjustment = adjustment.(float64)
-	}
-	if NodeType, err := getTypedVal(fmap["nodeType"]); err == nil {
-		n.NodeType = NodeType.(string)
-	}
-	if CPUCoreHours, err := getTypedVal(fmap["cpuCoreHours"]); err == nil {
-		n.CPUCoreHours = CPUCoreHours.(float64)
-	}
-	if RAMByteHours, err := getTypedVal(fmap["ramByteHours"]); err == nil {
-		n.RAMByteHours = RAMByteHours.(float64)
-	}
-	if GPUHours, err := getTypedVal(fmap["GPUHours"]); err == nil {
-		n.GPUHours = GPUHours.(float64)
-	}
-	if CPUCost, err := getTypedVal(fmap["cpuCost"]); err == nil {
-		n.CPUCost = CPUCost.(float64)
-	}
-	if GPUCost, err := getTypedVal(fmap["gpuCost"]); err == nil {
-		n.GPUCost = GPUCost.(float64)
-	}
-	if GPUCount, err := getTypedVal(fmap["gpuCount"]); err == nil {
-		n.GPUCount = GPUCount.(float64)
-	}
-	if RAMCost, err := getTypedVal(fmap["ramCost"]); err == nil {
-		n.RAMCost = RAMCost.(float64)
-	}
-	if Discount, err := getTypedVal(fmap["discount"]); err == nil {
-		n.Discount = Discount.(float64)
-	}
-	if Preemptible, err := getTypedVal(fmap["preemptible"]); err == nil {
-		n.Preemptible = Preemptible.(float64)
-	}
-
-	return nil
-}
-
 // String implements fmt.Stringer
 func (n *Node) String() string {
 	return toString(n)
@@ -2723,84 +2193,6 @@ func (lb *LoadBalancer) Equal(a Asset) bool {
 	return true
 }
 
-// MarshalJSON implements json.Marshal
-func (lb *LoadBalancer) MarshalJSON() ([]byte, error) {
-	buffer := bytes.NewBufferString("{")
-	jsonEncodeString(buffer, "type", lb.Type().String(), ",")
-	jsonEncode(buffer, "properties", lb.Properties(), ",")
-	jsonEncode(buffer, "labels", lb.Labels(), ",")
-	jsonEncode(buffer, "window", lb.Window(), ",")
-	jsonEncodeString(buffer, "start", lb.Start().Format(time.RFC3339), ",")
-	jsonEncodeString(buffer, "end", lb.End().Format(time.RFC3339), ",")
-	jsonEncodeFloat64(buffer, "minutes", lb.Minutes(), ",")
-	jsonEncodeFloat64(buffer, "adjustment", lb.Adjustment(), ",")
-	jsonEncodeFloat64(buffer, "totalCost", lb.TotalCost(), "")
-	buffer.WriteString("}")
-	return buffer.Bytes(), nil
-}
-
-func (lb *LoadBalancer) UnmarshalJSON(b []byte) error {
-
-	var f interface{}
-
-	err := json.Unmarshal(b, &f)
-	if err != nil {
-		return err
-	}
-
-	err = lb.InterfaceToLoadBalancer(f)
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// Converts interface{} to LoadBalancer, carrying over relevant fields
-func (lb *LoadBalancer) InterfaceToLoadBalancer(itf interface{}) error {
-
-	fmap := itf.(map[string]interface{})
-
-	// parse properties map to AssetProperties
-	fproperties := fmap["properties"].(map[string]interface{})
-	properties := toAssetProp(fproperties)
-
-	// parse labels map to AssetLabels
-	labels := make(map[string]string)
-	for k, v := range fmap["labels"].(map[string]interface{}) {
-		labels[k] = v.(string)
-	}
-
-	// parse start and end strings to time.Time
-	start, err := time.Parse(time.RFC3339, fmap["start"].(string))
-	if err != nil {
-		return err
-	}
-	end, err := time.Parse(time.RFC3339, fmap["end"].(string))
-	if err != nil {
-		return err
-	}
-
-	lb.properties = &properties
-	lb.labels = labels
-	lb.start = start
-	lb.end = end
-	lb.window = Window{
-		start: &start,
-		end:   &end,
-	}
-
-	if adjustment, err := getTypedVal(fmap["adjustment"]); err == nil {
-		lb.adjustment = adjustment.(float64)
-	}
-	if Cost, err := getTypedVal(fmap["totalCost"]); err == nil {
-		lb.Cost = Cost.(float64) - lb.adjustment
-	}
-
-	return nil
-
-}
-
 // String implements fmt.Stringer
 func (lb *LoadBalancer) String() string {
 	return toString(lb)
@@ -2994,80 +2386,6 @@ func (sa *SharedAsset) Equal(a Asset) bool {
 	return true
 }
 
-// MarshalJSON implements json.Marshaler
-func (sa *SharedAsset) MarshalJSON() ([]byte, error) {
-	buffer := bytes.NewBufferString("{")
-	jsonEncodeString(buffer, "type", sa.Type().String(), ",")
-	jsonEncode(buffer, "properties", sa.Properties(), ",")
-	jsonEncode(buffer, "labels", sa.Labels(), ",")
-	jsonEncode(buffer, "properties", sa.Properties(), ",")
-	jsonEncode(buffer, "labels", sa.Labels(), ",")
-	jsonEncode(buffer, "window", sa.Window(), ",")
-	jsonEncodeString(buffer, "start", sa.Start().Format(time.RFC3339), ",")
-	jsonEncodeString(buffer, "end", sa.End().Format(time.RFC3339), ",")
-	jsonEncodeFloat64(buffer, "minutes", sa.Minutes(), ",")
-	jsonEncodeFloat64(buffer, "totalCost", sa.TotalCost(), "")
-	buffer.WriteString("}")
-	return buffer.Bytes(), nil
-}
-
-func (sa *SharedAsset) UnmarshalJSON(b []byte) error {
-
-	var f interface{}
-
-	err := json.Unmarshal(b, &f)
-	if err != nil {
-		return err
-	}
-
-	err = sa.InterfaceToSharedAsset(f)
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// Converts interface{} to SharedAsset, carrying over relevant fields
-func (sa *SharedAsset) InterfaceToSharedAsset(itf interface{}) error {
-
-	fmap := itf.(map[string]interface{})
-
-	// parse properties map to AssetProperties
-	fproperties := fmap["properties"].(map[string]interface{})
-	properties := toAssetProp(fproperties)
-
-	// parse labels map to AssetLabels
-	labels := make(map[string]string)
-	for k, v := range fmap["labels"].(map[string]interface{}) {
-		labels[k] = v.(string)
-	}
-
-	// parse start and end strings to time.Time
-	start, err := time.Parse(time.RFC3339, fmap["start"].(string))
-	if err != nil {
-		return err
-	}
-	end, err := time.Parse(time.RFC3339, fmap["end"].(string))
-	if err != nil {
-		return err
-	}
-
-	sa.properties = &properties
-	sa.labels = labels
-	sa.window = Window{
-		start: &start,
-		end:   &end,
-	}
-
-	if Cost, err := getTypedVal(fmap["totalCost"]); err == nil {
-		sa.Cost = Cost.(float64)
-	}
-
-	return nil
-
-}
-
 // String implements fmt.Stringer
 func (sa *SharedAsset) String() string {
 	return toString(sa)
@@ -3080,129 +2398,6 @@ type AssetSetResponse struct {
 	assets map[string]Asset
 }
 
-// Unmarshals a marshaled AssetSet json into AssetSetResponse
-func (asr *AssetSetResponse) UnmarshalJSON(b []byte) error {
-
-	// gojson used here, as jsonitter UnmarshalJSON won't work with RawMessage
-	var assetMap map[string]*gojson.RawMessage
-
-	// Partial unmarshal to map of json RawMessage
-	err := gojson.Unmarshal(b, &assetMap)
-	if err != nil {
-		return err
-	}
-
-	newAssetMap := make(map[string]Asset)
-
-	// For each item in asset map, unmarshal to appropriate type
-	for key, rawMessage := range assetMap {
-
-		var f interface{}
-
-		err := json.Unmarshal(*rawMessage, &f)
-		if err != nil {
-			return err
-		}
-
-		fmap := f.(map[string]interface{})
-
-		switch t := fmap["type"]; t {
-		case "Cloud":
-
-			var ca Cloud
-			err := ca.InterfaceToCloud(f)
-
-			if err != nil {
-				return err
-			}
-
-			newAssetMap[key] = &ca
-
-		case "ClusterManagement":
-
-			var cm ClusterManagement
-			err := cm.InterfaceToClusterManagement(f)
-
-			if err != nil {
-				return err
-			}
-
-			newAssetMap[key] = &cm
-
-		case "Disk":
-
-			var d Disk
-			err := d.InterfaceToDisk(f)
-
-			if err != nil {
-				return err
-			}
-
-			newAssetMap[key] = &d
-
-		case "Network":
-
-			var nw Network
-			err := nw.InterfaceToNetwork(f)
-
-			if err != nil {
-				return err
-			}
-
-			newAssetMap[key] = &nw
-
-		case "Node":
-
-			var n Node
-			err := n.InterfaceToNode(f)
-
-			if err != nil {
-				return err
-			}
-
-			newAssetMap[key] = &n
-
-		case "LoadBalancer":
-
-			var lb LoadBalancer
-			err := lb.InterfaceToLoadBalancer(f)
-
-			if err != nil {
-				return err
-			}
-
-			newAssetMap[key] = &lb
-
-		case "Shared":
-
-			var sa SharedAsset
-			err := sa.InterfaceToSharedAsset(f)
-
-			if err != nil {
-				return err
-			}
-
-			newAssetMap[key] = &sa
-
-		default:
-
-			var a Any
-			err := a.InterfaceToAny(f)
-
-			if err != nil {
-				return err
-			}
-
-			newAssetMap[key] = &a
-
-		}
-	}
-
-	asr.assets = newAssetMap
-
-	return nil
-}
-
 // AssetSet stores a set of Assets, each with a unique name, that share
 // a window. An AssetSet is mutable, so treat it like a threadsafe map.
 type AssetSet struct {
@@ -3497,13 +2692,6 @@ func (as *AssetSet) Map() map[string]Asset {
 	return as.Clone().assets
 }
 
-// MarshalJSON JSON-encodes the AssetSet
-func (as *AssetSet) MarshalJSON() ([]byte, error) {
-	as.RLock()
-	defer as.RUnlock()
-	return json.Marshal(as.assets)
-}
-
 func (as *AssetSet) Set(asset Asset, aggregateBy []string) error {
 	if as.IsEmpty() {
 		as.Lock()
@@ -3822,71 +3010,3 @@ func contains(slice []string, item string) bool {
 	}
 	return false
 }
-
-// Creates an AssetProperties directly from map[string]interface{}
-func toAssetProp(fproperties map[string]interface{}) AssetProperties {
-	var properties AssetProperties
-
-	if category, v := fproperties["category"].(string); v {
-		properties.Category = category
-	}
-	if provider, v := fproperties["provider"].(string); v {
-		properties.Provider = provider
-	}
-	if account, v := fproperties["account"].(string); v {
-		properties.Account = account
-	}
-	if project, v := fproperties["project"].(string); v {
-		properties.Project = project
-	}
-	if service, v := fproperties["service"].(string); v {
-		properties.Service = service
-	}
-	if cluster, v := fproperties["cluster"].(string); v {
-		properties.Cluster = cluster
-	}
-	if name, v := fproperties["name"].(string); v {
-		properties.Name = name
-	}
-	if providerID, v := fproperties["providerID"].(string); v {
-		properties.ProviderID = providerID
-	}
-
-	return properties
-
-}
-
-// Creates an Breakdown directly from map[string]interface{}
-func toBreakdown(fproperties map[string]interface{}) Breakdown {
-	var breakdown Breakdown
-
-	if idle, v := fproperties["idle"].(float64); v {
-		breakdown.Idle = idle
-	}
-	if other, v := fproperties["other"].(float64); v {
-		breakdown.Other = other
-	}
-	if system, v := fproperties["system"].(float64); v {
-		breakdown.System = system
-	}
-	if user, v := fproperties["user"].(float64); v {
-		breakdown.User = user
-	}
-
-	return breakdown
-
-}
-
-// Not strictly nessesary, but cleans up the code and is a secondary check
-// for correct types
-func getTypedVal(itf interface{}) (interface{}, error) {
-	switch itf := itf.(type) {
-	case float64:
-		return float64(itf), nil
-	case string:
-		return string(itf), nil
-	default:
-		unktype := reflect.ValueOf(itf)
-		return nil, fmt.Errorf("Type %v is an invalid type", unktype)
-	}
-}

+ 913 - 0
pkg/kubecost/asset_unmarshal.go

@@ -0,0 +1,913 @@
+package kubecost
+
+import (
+	"bytes"
+	"fmt"
+	"reflect"
+	"time"
+
+	// gojson is default golang json, required for RawMessage decoding
+	gojson "encoding/json"
+
+	"github.com/kubecost/cost-model/pkg/util/json"
+)
+
+// Encoding and decoding logic for Asset types
+
+// Any marshal and unmarshal
+
+// MarshalJSON implements json.Marshaler
+func (a *Any) MarshalJSON() ([]byte, error) {
+	buffer := bytes.NewBufferString("{")
+	jsonEncode(buffer, "properties", a.Properties(), ",")
+	jsonEncode(buffer, "labels", a.Labels(), ",")
+	jsonEncode(buffer, "window", a.Window(), ",")
+	jsonEncodeString(buffer, "start", a.Start().Format(time.RFC3339), ",")
+	jsonEncodeString(buffer, "end", a.End().Format(time.RFC3339), ",")
+	jsonEncodeFloat64(buffer, "minutes", a.Minutes(), ",")
+	jsonEncodeFloat64(buffer, "adjustment", a.Adjustment(), ",")
+	jsonEncodeFloat64(buffer, "totalCost", a.TotalCost(), "")
+	buffer.WriteString("}")
+	return buffer.Bytes(), nil
+}
+
+func (a *Any) UnmarshalJSON(b []byte) error {
+
+	var f interface{}
+
+	err := json.Unmarshal(b, &f)
+	if err != nil {
+		return err
+	}
+
+	err = a.InterfaceToAny(f)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Converts interface{} to Any, carrying over relevant fields
+func (a *Any) InterfaceToAny(itf interface{}) error {
+
+	fmap := itf.(map[string]interface{})
+
+	// parse properties map to AssetProperties
+	fproperties := fmap["properties"].(map[string]interface{})
+	properties := toAssetProp(fproperties)
+
+	// parse labels map to AssetLabels
+	labels := make(map[string]string)
+	for k, v := range fmap["labels"].(map[string]interface{}) {
+		labels[k] = v.(string)
+	}
+
+	// parse start and end strings to time.Time
+	start, err := time.Parse(time.RFC3339, fmap["start"].(string))
+	if err != nil {
+		return err
+	}
+	end, err := time.Parse(time.RFC3339, fmap["end"].(string))
+	if err != nil {
+		return err
+	}
+
+	a.properties = &properties
+	a.labels = labels
+	a.start = start
+	a.end = end
+	a.window = Window{
+		start: &start,
+		end:   &end,
+	}
+
+	if adjustment, err := getTypedVal(fmap["adjustment"]); err == nil {
+		a.adjustment = adjustment.(float64)
+	}
+	if Cost, err := getTypedVal(fmap["totalCost"]); err == nil {
+		a.Cost = Cost.(float64) - a.adjustment
+	}
+
+	return nil
+}
+
+// Cloud marshal and unmarshal
+
+// MarshalJSON implements json.Marshaler
+func (ca *Cloud) MarshalJSON() ([]byte, error) {
+	buffer := bytes.NewBufferString("{")
+	jsonEncodeString(buffer, "type", ca.Type().String(), ",")
+	jsonEncode(buffer, "properties", ca.Properties(), ",")
+	jsonEncode(buffer, "labels", ca.Labels(), ",")
+	jsonEncode(buffer, "window", ca.Window(), ",")
+	jsonEncodeString(buffer, "start", ca.Start().Format(time.RFC3339), ",")
+	jsonEncodeString(buffer, "end", ca.End().Format(time.RFC3339), ",")
+	jsonEncodeFloat64(buffer, "minutes", ca.Minutes(), ",")
+	jsonEncodeFloat64(buffer, "adjustment", ca.Adjustment(), ",")
+	jsonEncodeFloat64(buffer, "credit", ca.Credit, ",")
+	jsonEncodeFloat64(buffer, "totalCost", ca.TotalCost(), "")
+	buffer.WriteString("}")
+	return buffer.Bytes(), nil
+}
+
+func (ca *Cloud) UnmarshalJSON(b []byte) error {
+
+	var f interface{}
+
+	err := json.Unmarshal(b, &f)
+	if err != nil {
+		return err
+	}
+
+	err = ca.InterfaceToCloud(f)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Converts interface{} to Cloud, carrying over relevant fields
+func (ca *Cloud) InterfaceToCloud(itf interface{}) error {
+
+	fmap := itf.(map[string]interface{})
+
+	// parse properties map to AssetProperties
+	fproperties := fmap["properties"].(map[string]interface{})
+	properties := toAssetProp(fproperties)
+
+	// parse labels map to AssetLabels
+	labels := make(map[string]string)
+	for k, v := range fmap["labels"].(map[string]interface{}) {
+		labels[k] = v.(string)
+	}
+
+	// parse start and end strings to time.Time
+	start, err := time.Parse(time.RFC3339, fmap["start"].(string))
+	if err != nil {
+		return err
+	}
+	end, err := time.Parse(time.RFC3339, fmap["end"].(string))
+	if err != nil {
+		return err
+	}
+
+	ca.properties = &properties
+	ca.labels = labels
+	ca.start = start
+	ca.end = end
+	ca.window = Window{
+		start: &start,
+		end:   &end,
+	}
+
+	if adjustment, err := getTypedVal(fmap["adjustment"]); err == nil {
+		ca.adjustment = adjustment.(float64)
+	}
+	if Credit, err := getTypedVal(fmap["credit"]); err == nil {
+		ca.Credit = Credit.(float64)
+	}
+	if Cost, err := getTypedVal(fmap["totalCost"]); err == nil {
+		ca.Cost = Cost.(float64) - ca.adjustment - ca.Credit
+	}
+
+	return nil
+}
+
+// ClusterManagement marshal and unmarshal
+
+// MarshalJSON implements json.Marshler
+func (cm *ClusterManagement) MarshalJSON() ([]byte, error) {
+	buffer := bytes.NewBufferString("{")
+	jsonEncodeString(buffer, "type", cm.Type().String(), ",")
+	jsonEncode(buffer, "properties", cm.Properties(), ",")
+	jsonEncode(buffer, "labels", cm.Labels(), ",")
+	jsonEncode(buffer, "window", cm.Window(), ",")
+	jsonEncodeString(buffer, "start", cm.Start().Format(time.RFC3339), ",")
+	jsonEncodeString(buffer, "end", cm.End().Format(time.RFC3339), ",")
+	jsonEncodeFloat64(buffer, "minutes", cm.Minutes(), ",")
+	jsonEncodeFloat64(buffer, "totalCost", cm.TotalCost(), "")
+	buffer.WriteString("}")
+	return buffer.Bytes(), nil
+}
+
+func (cm *ClusterManagement) UnmarshalJSON(b []byte) error {
+
+	var f interface{}
+
+	err := json.Unmarshal(b, &f)
+	if err != nil {
+		return err
+	}
+
+	err = cm.InterfaceToClusterManagement(f)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Converts interface{} to ClusterManagement, carrying over relevant fields
+func (cm *ClusterManagement) InterfaceToClusterManagement(itf interface{}) error {
+
+	fmap := itf.(map[string]interface{})
+
+	// parse properties map to AssetProperties
+	fproperties := fmap["properties"].(map[string]interface{})
+	properties := toAssetProp(fproperties)
+
+	// parse labels map to AssetLabels
+	labels := make(map[string]string)
+	for k, v := range fmap["labels"].(map[string]interface{}) {
+		labels[k] = v.(string)
+	}
+
+	// parse start and end strings to time.Time
+	start, err := time.Parse(time.RFC3339, fmap["start"].(string))
+	if err != nil {
+		return err
+	}
+	end, err := time.Parse(time.RFC3339, fmap["end"].(string))
+	if err != nil {
+		return err
+	}
+
+	cm.properties = &properties
+	cm.labels = labels
+	cm.window = Window{
+		start: &start,
+		end:   &end,
+	}
+
+	if Cost, err := getTypedVal(fmap["totalCost"]); err == nil {
+		cm.Cost = Cost.(float64)
+	}
+
+	return nil
+}
+
+// Disk marshal and unmarshal
+
+// MarshalJSON implements the json.Marshaler interface
+func (d *Disk) MarshalJSON() ([]byte, error) {
+	buffer := bytes.NewBufferString("{")
+	jsonEncodeString(buffer, "type", d.Type().String(), ",")
+	jsonEncode(buffer, "properties", d.Properties(), ",")
+	jsonEncode(buffer, "labels", d.Labels(), ",")
+	jsonEncode(buffer, "window", d.Window(), ",")
+	jsonEncodeString(buffer, "start", d.Start().Format(time.RFC3339), ",")
+	jsonEncodeString(buffer, "end", d.End().Format(time.RFC3339), ",")
+	jsonEncodeFloat64(buffer, "minutes", d.Minutes(), ",")
+	jsonEncodeFloat64(buffer, "byteHours", d.ByteHours, ",")
+	jsonEncodeFloat64(buffer, "bytes", d.Bytes(), ",")
+	jsonEncode(buffer, "breakdown", d.Breakdown, ",")
+	jsonEncodeFloat64(buffer, "adjustment", d.Adjustment(), ",")
+	jsonEncodeFloat64(buffer, "totalCost", d.TotalCost(), "")
+	buffer.WriteString("}")
+	return buffer.Bytes(), nil
+}
+
+func (d *Disk) UnmarshalJSON(b []byte) error {
+
+	var f interface{}
+
+	err := json.Unmarshal(b, &f)
+	if err != nil {
+		return err
+	}
+
+	err = d.InterfaceToDisk(f)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Converts interface{} to Disk, carrying over relevant fields
+func (d *Disk) InterfaceToDisk(itf interface{}) error {
+
+	fmap := itf.(map[string]interface{})
+
+	// parse properties map to AssetProperties
+	fproperties := fmap["properties"].(map[string]interface{})
+	properties := toAssetProp(fproperties)
+
+	// parse labels map to AssetLabels
+	labels := make(map[string]string)
+	for k, v := range fmap["labels"].(map[string]interface{}) {
+		labels[k] = v.(string)
+	}
+
+	// parse start and end strings to time.Time
+	start, err := time.Parse(time.RFC3339, fmap["start"].(string))
+	if err != nil {
+		return err
+	}
+	end, err := time.Parse(time.RFC3339, fmap["end"].(string))
+	if err != nil {
+		return err
+	}
+
+	fbreakdown := fmap["breakdown"].(map[string]interface{})
+
+	breakdown := toBreakdown(fbreakdown)
+
+	d.properties = &properties
+	d.labels = labels
+	d.start = start
+	d.end = end
+	d.window = Window{
+		start: &start,
+		end:   &end,
+	}
+	d.Breakdown = &breakdown
+
+	if adjustment, err := getTypedVal(fmap["adjustment"]); err == nil {
+		d.adjustment = adjustment.(float64)
+	}
+	if Cost, err := getTypedVal(fmap["totalCost"]); err == nil {
+		d.Cost = Cost.(float64) - d.adjustment
+	}
+	if ByteHours, err := getTypedVal(fmap["byteHours"]); err == nil {
+		d.ByteHours = ByteHours.(float64)
+	}
+
+	// d.Local is not marhsaled, and cannot be calculated from marshaled values.
+	// Currently, it is just ignored and not set in the resulting unmarshal to Disk
+	//  be aware that this means a resulting Disk from an unmarshal is therefore NOT
+	// equal to the originally marshaled Disk.
+
+	return nil
+
+}
+
+// Network marshal and unmarshal
+
+// MarshalJSON implements json.Marshal interface
+func (n *Network) MarshalJSON() ([]byte, error) {
+	buffer := bytes.NewBufferString("{")
+	jsonEncodeString(buffer, "type", n.Type().String(), ",")
+	jsonEncode(buffer, "properties", n.Properties(), ",")
+	jsonEncode(buffer, "labels", n.Labels(), ",")
+	jsonEncode(buffer, "window", n.Window(), ",")
+	jsonEncodeString(buffer, "start", n.Start().Format(time.RFC3339), ",")
+	jsonEncodeString(buffer, "end", n.End().Format(time.RFC3339), ",")
+	jsonEncodeFloat64(buffer, "minutes", n.Minutes(), ",")
+	jsonEncodeFloat64(buffer, "adjustment", n.Adjustment(), ",")
+	jsonEncodeFloat64(buffer, "totalCost", n.TotalCost(), "")
+	buffer.WriteString("}")
+	return buffer.Bytes(), nil
+}
+
+func (n *Network) UnmarshalJSON(b []byte) error {
+
+	var f interface{}
+
+	err := json.Unmarshal(b, &f)
+	if err != nil {
+		return err
+	}
+
+	err = n.InterfaceToNetwork(f)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Converts interface{} to Network, carrying over relevant fields
+func (n *Network) InterfaceToNetwork(itf interface{}) error {
+
+	fmap := itf.(map[string]interface{})
+
+	// parse properties map to AssetProperties
+	fproperties := fmap["properties"].(map[string]interface{})
+	properties := toAssetProp(fproperties)
+
+	// parse labels map to AssetLabels
+	labels := make(map[string]string)
+	for k, v := range fmap["labels"].(map[string]interface{}) {
+		labels[k] = v.(string)
+	}
+
+	// parse start and end strings to time.Time
+	start, err := time.Parse(time.RFC3339, fmap["start"].(string))
+	if err != nil {
+		return err
+	}
+	end, err := time.Parse(time.RFC3339, fmap["end"].(string))
+	if err != nil {
+		return err
+	}
+
+	n.properties = &properties
+	n.labels = labels
+	n.start = start
+	n.end = end
+	n.window = Window{
+		start: &start,
+		end:   &end,
+	}
+
+	if adjustment, err := getTypedVal(fmap["adjustment"]); err == nil {
+		n.adjustment = adjustment.(float64)
+	}
+	if Cost, err := getTypedVal(fmap["totalCost"]); err == nil {
+		n.Cost = Cost.(float64) - n.adjustment
+	}
+
+	return nil
+
+}
+
+// Node marshal and unmarshal
+
+// MarshalJSON implements json.Marshal interface
+func (n *Node) MarshalJSON() ([]byte, error) {
+	buffer := bytes.NewBufferString("{")
+	jsonEncodeString(buffer, "type", n.Type().String(), ",")
+	jsonEncode(buffer, "properties", n.Properties(), ",")
+	jsonEncode(buffer, "labels", n.Labels(), ",")
+	jsonEncode(buffer, "window", n.Window(), ",")
+	jsonEncodeString(buffer, "start", n.Start().Format(time.RFC3339), ",")
+	jsonEncodeString(buffer, "end", n.End().Format(time.RFC3339), ",")
+	jsonEncodeFloat64(buffer, "minutes", n.Minutes(), ",")
+	jsonEncodeString(buffer, "nodeType", n.NodeType, ",")
+	jsonEncodeFloat64(buffer, "cpuCores", n.CPUCores(), ",")
+	jsonEncodeFloat64(buffer, "ramBytes", n.RAMBytes(), ",")
+	jsonEncodeFloat64(buffer, "cpuCoreHours", n.CPUCoreHours, ",")
+	jsonEncodeFloat64(buffer, "ramByteHours", n.RAMByteHours, ",")
+	jsonEncodeFloat64(buffer, "GPUHours", n.GPUHours, ",")
+	jsonEncode(buffer, "cpuBreakdown", n.CPUBreakdown, ",")
+	jsonEncode(buffer, "ramBreakdown", n.RAMBreakdown, ",")
+	jsonEncodeFloat64(buffer, "preemptible", n.Preemptible, ",")
+	jsonEncodeFloat64(buffer, "discount", n.Discount, ",")
+	jsonEncodeFloat64(buffer, "cpuCost", n.CPUCost, ",")
+	jsonEncodeFloat64(buffer, "gpuCost", n.GPUCost, ",")
+	jsonEncodeFloat64(buffer, "gpuCount", n.GPUs(), ",")
+	jsonEncodeFloat64(buffer, "ramCost", n.RAMCost, ",")
+	jsonEncodeFloat64(buffer, "adjustment", n.Adjustment(), ",")
+	jsonEncodeFloat64(buffer, "totalCost", n.TotalCost(), "")
+	buffer.WriteString("}")
+	return buffer.Bytes(), nil
+}
+
+func (n *Node) UnmarshalJSON(b []byte) error {
+
+	var f interface{}
+
+	err := json.Unmarshal(b, &f)
+	if err != nil {
+		return err
+	}
+
+	err = n.InterfaceToNode(f)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Converts interface{} to Node, carrying over relevant fields
+func (n *Node) InterfaceToNode(itf interface{}) error {
+
+	fmap := itf.(map[string]interface{})
+
+	// parse properties map to AssetProperties
+	fproperties := fmap["properties"].(map[string]interface{})
+	properties := toAssetProp(fproperties)
+
+	// parse labels map to AssetLabels
+	labels := make(map[string]string)
+	for k, v := range fmap["labels"].(map[string]interface{}) {
+		labels[k] = v.(string)
+	}
+
+	// parse start and end strings to time.Time
+	start, err := time.Parse(time.RFC3339, fmap["start"].(string))
+	if err != nil {
+		return err
+	}
+	end, err := time.Parse(time.RFC3339, fmap["end"].(string))
+	if err != nil {
+		return err
+	}
+
+	fcpuBreakdown := fmap["cpuBreakdown"].(map[string]interface{})
+	framBreakdown := fmap["ramBreakdown"].(map[string]interface{})
+
+	cpuBreakdown := toBreakdown(fcpuBreakdown)
+	ramBreakdown := toBreakdown(framBreakdown)
+
+	n.properties = &properties
+	n.labels = labels
+	n.start = start
+	n.end = end
+	n.window = Window{
+		start: &start,
+		end:   &end,
+	}
+	n.CPUBreakdown = &cpuBreakdown
+	n.RAMBreakdown = &ramBreakdown
+
+	if adjustment, err := getTypedVal(fmap["adjustment"]); err == nil {
+		n.adjustment = adjustment.(float64)
+	}
+	if NodeType, err := getTypedVal(fmap["nodeType"]); err == nil {
+		n.NodeType = NodeType.(string)
+	}
+	if CPUCoreHours, err := getTypedVal(fmap["cpuCoreHours"]); err == nil {
+		n.CPUCoreHours = CPUCoreHours.(float64)
+	}
+	if RAMByteHours, err := getTypedVal(fmap["ramByteHours"]); err == nil {
+		n.RAMByteHours = RAMByteHours.(float64)
+	}
+	if GPUHours, err := getTypedVal(fmap["GPUHours"]); err == nil {
+		n.GPUHours = GPUHours.(float64)
+	}
+	if CPUCost, err := getTypedVal(fmap["cpuCost"]); err == nil {
+		n.CPUCost = CPUCost.(float64)
+	}
+	if GPUCost, err := getTypedVal(fmap["gpuCost"]); err == nil {
+		n.GPUCost = GPUCost.(float64)
+	}
+	if GPUCount, err := getTypedVal(fmap["gpuCount"]); err == nil {
+		n.GPUCount = GPUCount.(float64)
+	}
+	if RAMCost, err := getTypedVal(fmap["ramCost"]); err == nil {
+		n.RAMCost = RAMCost.(float64)
+	}
+	if Discount, err := getTypedVal(fmap["discount"]); err == nil {
+		n.Discount = Discount.(float64)
+	}
+	if Preemptible, err := getTypedVal(fmap["preemptible"]); err == nil {
+		n.Preemptible = Preemptible.(float64)
+	}
+
+	return nil
+}
+
+// Loadbalancer marshal and unmarshal
+
+// MarshalJSON implements json.Marshal
+func (lb *LoadBalancer) MarshalJSON() ([]byte, error) {
+	buffer := bytes.NewBufferString("{")
+	jsonEncodeString(buffer, "type", lb.Type().String(), ",")
+	jsonEncode(buffer, "properties", lb.Properties(), ",")
+	jsonEncode(buffer, "labels", lb.Labels(), ",")
+	jsonEncode(buffer, "window", lb.Window(), ",")
+	jsonEncodeString(buffer, "start", lb.Start().Format(time.RFC3339), ",")
+	jsonEncodeString(buffer, "end", lb.End().Format(time.RFC3339), ",")
+	jsonEncodeFloat64(buffer, "minutes", lb.Minutes(), ",")
+	jsonEncodeFloat64(buffer, "adjustment", lb.Adjustment(), ",")
+	jsonEncodeFloat64(buffer, "totalCost", lb.TotalCost(), "")
+	buffer.WriteString("}")
+	return buffer.Bytes(), nil
+}
+
+func (lb *LoadBalancer) UnmarshalJSON(b []byte) error {
+
+	var f interface{}
+
+	err := json.Unmarshal(b, &f)
+	if err != nil {
+		return err
+	}
+
+	err = lb.InterfaceToLoadBalancer(f)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Converts interface{} to LoadBalancer, carrying over relevant fields
+func (lb *LoadBalancer) InterfaceToLoadBalancer(itf interface{}) error {
+
+	fmap := itf.(map[string]interface{})
+
+	// parse properties map to AssetProperties
+	fproperties := fmap["properties"].(map[string]interface{})
+	properties := toAssetProp(fproperties)
+
+	// parse labels map to AssetLabels
+	labels := make(map[string]string)
+	for k, v := range fmap["labels"].(map[string]interface{}) {
+		labels[k] = v.(string)
+	}
+
+	// parse start and end strings to time.Time
+	start, err := time.Parse(time.RFC3339, fmap["start"].(string))
+	if err != nil {
+		return err
+	}
+	end, err := time.Parse(time.RFC3339, fmap["end"].(string))
+	if err != nil {
+		return err
+	}
+
+	lb.properties = &properties
+	lb.labels = labels
+	lb.start = start
+	lb.end = end
+	lb.window = Window{
+		start: &start,
+		end:   &end,
+	}
+
+	if adjustment, err := getTypedVal(fmap["adjustment"]); err == nil {
+		lb.adjustment = adjustment.(float64)
+	}
+	if Cost, err := getTypedVal(fmap["totalCost"]); err == nil {
+		lb.Cost = Cost.(float64) - lb.adjustment
+	}
+
+	return nil
+
+}
+
+// SharedAsset marshal and unmarshal
+
+// MarshalJSON implements json.Marshaler
+func (sa *SharedAsset) MarshalJSON() ([]byte, error) {
+	buffer := bytes.NewBufferString("{")
+	jsonEncodeString(buffer, "type", sa.Type().String(), ",")
+	jsonEncode(buffer, "properties", sa.Properties(), ",")
+	jsonEncode(buffer, "labels", sa.Labels(), ",")
+	jsonEncode(buffer, "properties", sa.Properties(), ",")
+	jsonEncode(buffer, "labels", sa.Labels(), ",")
+	jsonEncode(buffer, "window", sa.Window(), ",")
+	jsonEncodeString(buffer, "start", sa.Start().Format(time.RFC3339), ",")
+	jsonEncodeString(buffer, "end", sa.End().Format(time.RFC3339), ",")
+	jsonEncodeFloat64(buffer, "minutes", sa.Minutes(), ",")
+	jsonEncodeFloat64(buffer, "totalCost", sa.TotalCost(), "")
+	buffer.WriteString("}")
+	return buffer.Bytes(), nil
+}
+
+func (sa *SharedAsset) UnmarshalJSON(b []byte) error {
+
+	var f interface{}
+
+	err := json.Unmarshal(b, &f)
+	if err != nil {
+		return err
+	}
+
+	err = sa.InterfaceToSharedAsset(f)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Converts interface{} to SharedAsset, carrying over relevant fields
+func (sa *SharedAsset) InterfaceToSharedAsset(itf interface{}) error {
+
+	fmap := itf.(map[string]interface{})
+
+	// parse properties map to AssetProperties
+	fproperties := fmap["properties"].(map[string]interface{})
+	properties := toAssetProp(fproperties)
+
+	// parse labels map to AssetLabels
+	labels := make(map[string]string)
+	for k, v := range fmap["labels"].(map[string]interface{}) {
+		labels[k] = v.(string)
+	}
+
+	// parse start and end strings to time.Time
+	start, err := time.Parse(time.RFC3339, fmap["start"].(string))
+	if err != nil {
+		return err
+	}
+	end, err := time.Parse(time.RFC3339, fmap["end"].(string))
+	if err != nil {
+		return err
+	}
+
+	sa.properties = &properties
+	sa.labels = labels
+	sa.window = Window{
+		start: &start,
+		end:   &end,
+	}
+
+	if Cost, err := getTypedVal(fmap["totalCost"]); err == nil {
+		sa.Cost = Cost.(float64)
+	}
+
+	return nil
+
+}
+
+// AssetSet marshal
+
+// MarshalJSON JSON-encodes the AssetSet
+func (as *AssetSet) MarshalJSON() ([]byte, error) {
+	as.RLock()
+	defer as.RUnlock()
+	return json.Marshal(as.assets)
+}
+
+// AssetSetResponse for unmarshaling of AssetSet.assets into AssetSet
+
+// Unmarshals a marshaled AssetSet json into AssetSetResponse
+func (asr *AssetSetResponse) UnmarshalJSON(b []byte) error {
+
+	// gojson used here, as jsonitter UnmarshalJSON won't work with RawMessage
+	var assetMap map[string]*gojson.RawMessage
+
+	// Partial unmarshal to map of json RawMessage
+	err := gojson.Unmarshal(b, &assetMap)
+	if err != nil {
+		return err
+	}
+
+	newAssetMap := make(map[string]Asset)
+
+	// For each item in asset map, unmarshal to appropriate type
+	for key, rawMessage := range assetMap {
+
+		var f interface{}
+
+		err := json.Unmarshal(*rawMessage, &f)
+		if err != nil {
+			return err
+		}
+
+		fmap := f.(map[string]interface{})
+
+		switch t := fmap["type"]; t {
+		case "Cloud":
+
+			var ca Cloud
+			err := ca.InterfaceToCloud(f)
+
+			if err != nil {
+				return err
+			}
+
+			newAssetMap[key] = &ca
+
+		case "ClusterManagement":
+
+			var cm ClusterManagement
+			err := cm.InterfaceToClusterManagement(f)
+
+			if err != nil {
+				return err
+			}
+
+			newAssetMap[key] = &cm
+
+		case "Disk":
+
+			var d Disk
+			err := d.InterfaceToDisk(f)
+
+			if err != nil {
+				return err
+			}
+
+			newAssetMap[key] = &d
+
+		case "Network":
+
+			var nw Network
+			err := nw.InterfaceToNetwork(f)
+
+			if err != nil {
+				return err
+			}
+
+			newAssetMap[key] = &nw
+
+		case "Node":
+
+			var n Node
+			err := n.InterfaceToNode(f)
+
+			if err != nil {
+				return err
+			}
+
+			newAssetMap[key] = &n
+
+		case "LoadBalancer":
+
+			var lb LoadBalancer
+			err := lb.InterfaceToLoadBalancer(f)
+
+			if err != nil {
+				return err
+			}
+
+			newAssetMap[key] = &lb
+
+		case "Shared":
+
+			var sa SharedAsset
+			err := sa.InterfaceToSharedAsset(f)
+
+			if err != nil {
+				return err
+			}
+
+			newAssetMap[key] = &sa
+
+		default:
+
+			var a Any
+			err := a.InterfaceToAny(f)
+
+			if err != nil {
+				return err
+			}
+
+			newAssetMap[key] = &a
+
+		}
+	}
+
+	asr.assets = newAssetMap
+
+	return nil
+}
+
+// Extra decoding util functions, for clarity
+
+// Creates an AssetProperties directly from map[string]interface{}
+func toAssetProp(fproperties map[string]interface{}) AssetProperties {
+	var properties AssetProperties
+
+	if category, v := fproperties["category"].(string); v {
+		properties.Category = category
+	}
+	if provider, v := fproperties["provider"].(string); v {
+		properties.Provider = provider
+	}
+	if account, v := fproperties["account"].(string); v {
+		properties.Account = account
+	}
+	if project, v := fproperties["project"].(string); v {
+		properties.Project = project
+	}
+	if service, v := fproperties["service"].(string); v {
+		properties.Service = service
+	}
+	if cluster, v := fproperties["cluster"].(string); v {
+		properties.Cluster = cluster
+	}
+	if name, v := fproperties["name"].(string); v {
+		properties.Name = name
+	}
+	if providerID, v := fproperties["providerID"].(string); v {
+		properties.ProviderID = providerID
+	}
+
+	return properties
+
+}
+
+// Creates an Breakdown directly from map[string]interface{}
+func toBreakdown(fproperties map[string]interface{}) Breakdown {
+	var breakdown Breakdown
+
+	if idle, v := fproperties["idle"].(float64); v {
+		breakdown.Idle = idle
+	}
+	if other, v := fproperties["other"].(float64); v {
+		breakdown.Other = other
+	}
+	if system, v := fproperties["system"].(float64); v {
+		breakdown.System = system
+	}
+	if user, v := fproperties["user"].(float64); v {
+		breakdown.User = user
+	}
+
+	return breakdown
+
+}
+
+// Not strictly nessesary, but cleans up the code and is a secondary check
+// for correct types
+func getTypedVal(itf interface{}) (interface{}, error) {
+	switch itf := itf.(type) {
+	case float64:
+		return float64(itf), nil
+	case string:
+		return string(itf), nil
+	default:
+		unktype := reflect.ValueOf(itf)
+		return nil, fmt.Errorf("Type %v is an invalid type", unktype)
+	}
+}