fix: add compatibility logic for empty array when unmarshal json string to route (#374)

diff --git a/pkg/apisix/resource_test.go b/pkg/apisix/resource_test.go
index 51ea1eb..fe98958 100644
--- a/pkg/apisix/resource_test.go
+++ b/pkg/apisix/resource_test.go
@@ -18,6 +18,7 @@
 	"encoding/json"
 	"testing"
 
+	v1 "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -74,3 +75,24 @@
 	assert.Equal(t, r.Methods[1], "POST")
 	assert.Equal(t, r.Name, "unknown")
 }
+
+func TestRouteVarsUnmarshalJSONCompatibility(t *testing.T) {
+	var route v1.Route
+	data := `{"vars":{}}`
+	err := json.Unmarshal([]byte(data), &route)
+	assert.Nil(t, err)
+
+	data = `{"vars":{"a":"b"}}`
+	err = json.Unmarshal([]byte(data), &route)
+	assert.Equal(t, err.Error(), "unexpected non-empty object")
+
+	data = `{"vars":[]}`
+	err = json.Unmarshal([]byte(data), &route)
+	assert.Nil(t, err)
+
+	data = `{"vars":[["http_a","==","b"]]}`
+	err = json.Unmarshal([]byte(data), &route)
+	assert.Equal(t, "http_a", route.Vars[0][0].StrVal)
+	assert.Equal(t, "==", route.Vars[0][1].StrVal)
+	assert.Equal(t, "b", route.Vars[0][2].StrVal)
+}
diff --git a/pkg/apisix/route.go b/pkg/apisix/route.go
index 21561c1..c724e79 100644
--- a/pkg/apisix/route.go
+++ b/pkg/apisix/route.go
@@ -92,6 +92,7 @@
 		log.Errorw("failed to convert route item",
 			zap.String("url", r.url),
 			zap.String("route_key", resp.Item.Key),
+			zap.String("route_value", string(resp.Item.Value)),
 			zap.Error(err),
 		)
 		return nil, err
@@ -124,6 +125,7 @@
 			log.Errorw("failed to convert route item",
 				zap.String("url", r.url),
 				zap.String("route_key", item.Key),
+				zap.String("route_value", string(item.Value)),
 				zap.Error(err),
 			)
 			return nil, err
diff --git a/pkg/types/apisix/v1/types.go b/pkg/types/apisix/v1/types.go
index ec4cf2e..b660d3b 100644
--- a/pkg/types/apisix/v1/types.go
+++ b/pkg/types/apisix/v1/types.go
@@ -82,16 +82,38 @@
 type Route struct {
 	Metadata `json:",inline" yaml:",inline"`
 
-	Host        string            `json:"host,omitempty" yaml:"host,omitempty"`
-	Hosts       []string          `json:"hosts,omitempty" yaml:"hosts,omitempty"`
-	Uri         string            `json:"uri,omitempty" yaml:"uri,omitempty"`
-	Priority    int               `json:"priority,omitempty" yaml:"priority,omitempty"`
-	Vars        [][]StringOrSlice `json:"vars,omitempty" yaml:"vars,omitempty"`
-	Uris        []string          `json:"uris,omitempty" yaml:"uris,omitempty"`
-	Methods     []string          `json:"methods,omitempty" yaml:"methods,omitempty"`
-	RemoteAddrs []string          `json:"remote_addrs,omitempty" yaml:"remote_addrs,omitempty"`
-	UpstreamId  string            `json:"upstream_id,omitempty" yaml:"upstream_id,omitempty"`
-	Plugins     Plugins           `json:"plugins,omitempty" yaml:"plugins,omitempty"`
+	Host        string   `json:"host,omitempty" yaml:"host,omitempty"`
+	Hosts       []string `json:"hosts,omitempty" yaml:"hosts,omitempty"`
+	Uri         string   `json:"uri,omitempty" yaml:"uri,omitempty"`
+	Priority    int      `json:"priority,omitempty" yaml:"priority,omitempty"`
+	Vars        Vars     `json:"vars,omitempty" yaml:"vars,omitempty"`
+	Uris        []string `json:"uris,omitempty" yaml:"uris,omitempty"`
+	Methods     []string `json:"methods,omitempty" yaml:"methods,omitempty"`
+	RemoteAddrs []string `json:"remote_addrs,omitempty" yaml:"remote_addrs,omitempty"`
+	UpstreamId  string   `json:"upstream_id,omitempty" yaml:"upstream_id,omitempty"`
+	Plugins     Plugins  `json:"plugins,omitempty" yaml:"plugins,omitempty"`
+}
+
+// Vars represents the route match expressions of APISIX.
+type Vars [][]StringOrSlice
+
+// UnmarshalJSON implements json.Unmarshaler interface.
+// lua-cjson doesn't distinguish empty array and table,
+// and by default empty array will be encoded as '{}'.
+// We have to maintain the compatibility.
+func (vars *Vars) UnmarshalJSON(p []byte) error {
+	if p[0] == '{' {
+		if len(p) != 2 {
+			return errors.New("unexpected non-empty object")
+		}
+		return nil
+	}
+	var data [][]StringOrSlice
+	if err := json.Unmarshal(p, &data); err != nil {
+		return err
+	}
+	*vars = data
+	return nil
 }
 
 // StringOrSlice represents a string or a string slice.