Repository: lyraproj/hiera
Pull request #50 (2019-11-04 11:36:58)

Use proper JSON data deserialization
Switch to use the pcore deserializer for JSON as it is capable of
handling all sorts of data, even empty maps.

This fixes the submap problem reported in #47.


Patch:
diff --git a/hiera/render.go b/hiera/render.go
index 16ddd3b..6b28d82 100644
--- a/hiera/render.go
+++ b/hiera/render.go
@@ -1,13 +1,12 @@
 package hiera
 
 import (
-	"encoding/json"
 	"fmt"
 	"io"
 
-	"github.com/lyraproj/pcore/serialization"
-
+	"github.com/lyraproj/pcore/pcore"
 	"github.com/lyraproj/pcore/px"
+	"github.com/lyraproj/pcore/serialization"
 	"github.com/lyraproj/pcore/types"
 	"github.com/lyraproj/pcore/utils"
 	"gopkg.in/yaml.v3"
@@ -25,34 +24,35 @@ const (
 func Render(c px.Context, renderAs RenderName, value px.Value, out io.Writer) {
 	switch renderAs {
 	case YAML, JSON:
-		var v interface{}
+		var v px.Value
 		if !value.Equals(px.Undef, nil) {
 			// Convert value to rich data format
 			ser := serialization.NewSerializer(c, px.EmptyMap)
 			dc := px.NewCollector()
 			ser.Convert(value, dc)
-			rf := c.Reflector().Reflect(dc.Value())
+			v = dc.Value()
+		}
+
+		if renderAs == `yaml` {
+			rf := c.Reflector().Reflect(v)
+			var iv interface{}
 			if rf.IsValid() && rf.CanInterface() {
-				v = rf.Interface()
+				iv = rf.Interface()
 			} else {
-				v = value.String()
+				iv = value.String()
 			}
-		}
-		var bs []byte
-		var err error
-		if renderAs == `yaml` {
-			bs, err = yaml.Marshal(v)
-		} else {
-			// JSON is not able to handle the result of reflecting an empty untyped map.
-			if em, ok := v.(map[interface{}]interface{}); ok && len(em) == 0 {
-				v = map[string]interface{}{}
+			bs, err := yaml.Marshal(iv)
+			utils.WriteString(out, string(bs))
+			if err != nil {
+				panic(err)
 			}
-			bs, err = json.Marshal(v)
-		}
-		if err != nil {
-			panic(err)
+		} else {
+			he := make([]*types.HashEntry, 0, 2)
+			he = append(he, types.WrapHashEntry2(`rich_data`, types.BooleanFalse))
+			he = append(he, types.WrapHashEntry2(`local_reference`, types.BooleanFalse))
+			serialization.NewSerializer(pcore.RootContext(), types.WrapHash(he)).Convert(value, serialization.NewJsonStreamer(out))
+			utils.WriteByte(out, '\n')
 		}
-		utils.WriteString(out, string(bs))
 	case Binary:
 		bi := types.CoerceTo(c, `lookup value`, types.DefaultBinaryType(), value).(*types.Binary)
 		_, err := out.Write(bi.Bytes())


Test patch:
diff --git a/lookup/lookup_test.go b/lookup/lookup_test.go
index c60a0f2..904dc74 100644
--- a/lookup/lookup_test.go
+++ b/lookup/lookup_test.go
@@ -42,7 +42,7 @@ func TestLookup_defaultHash(t *testing.T) {
 func TestLookup_defaultHash_json(t *testing.T) {
 	result, err := cli.ExecuteLookup(`--default`, `{ x => 'a', y => 9 }`, `--type`, `Hash[String,Variant[String,Integer]]`, `--render-as`, `json`, `foo`)
 	require.NoError(t, err)
-	require.Equal(t, `{"x":"a","y":9}`, string(result))
+	require.Equal(t, "{\"x\":\"a\",\"y\":9}\n", string(result))
 }
 
 func TestLookup_defaultString_s(t *testing.T) {
@@ -115,7 +115,23 @@ func TestLookup_emptyMap(t *testing.T) {
 	inTestdata(func() {
 		result, err := cli.ExecuteLookup(`--config`, `empty_map.yaml`, `--render-as`, `json`, `empty_map`)
 		require.NoError(t, err)
-		require.Equal(t, "{}", string(result))
+		require.Equal(t, "{}\n", string(result))
+	})
+}
+
+func TestLookup_emptySubMap(t *testing.T) {
+	inTestdata(func() {
+		result, err := cli.ExecuteLookup(`--config`, `empty_map.yaml`, `--render-as`, `json`, `empty_sub_map`)
+		require.NoError(t, err)
+		require.Equal(t, "{\"x\":\"the x\",\"empty\":{}}\n", string(result))
+	})
+}
+
+func TestLookup_emptySubMapInArray(t *testing.T) {
+	inTestdata(func() {
+		result, err := cli.ExecuteLookup(`--config`, `empty_map.yaml`, `--render-as`, `json`, `empty_sub_map_in_array`)
+		require.NoError(t, err)
+		require.Equal(t, "[{}]\n", string(result))
 	})
 }
 
diff --git a/lookup/testdata/hiera/empty_map.yaml b/lookup/testdata/hiera/empty_map.yaml
index 2e81f50..8600ec0 100644
--- a/lookup/testdata/hiera/empty_map.yaml
+++ b/lookup/testdata/hiera/empty_map.yaml
@@ -1,1 +1,8 @@
-empty_map: {}
\ No newline at end of file
+empty_map: {}
+
+empty_sub_map:
+  x: the x
+  empty: {}
+
+empty_sub_map_in_array:
+  - {}