From 00b0546be819518c1d1582ba7f791c310b826b76 Mon Sep 17 00:00:00 2001
From: Jan Semmelink <jan@uafrica.com>
Date: Thu, 25 Nov 2021 15:50:21 +0200
Subject: [PATCH] Use Scan() method before json.Unmarshal for param values

---
 struct_utils/named_values_to_struct.go | 40 ++++++++++++++++++--------
 1 file changed, 28 insertions(+), 12 deletions(-)

diff --git a/struct_utils/named_values_to_struct.go b/struct_utils/named_values_to_struct.go
index 81c5836..b8f2142 100644
--- a/struct_utils/named_values_to_struct.go
+++ b/struct_utils/named_values_to_struct.go
@@ -1,6 +1,7 @@
 package struct_utils
 
 import (
+	"database/sql"
 	"encoding/csv"
 	"encoding/json"
 	"reflect"
@@ -284,19 +285,34 @@ func unmarshalValue(v interface{}, t reflect.Type) (reflect.Value, error) {
 	newValuePtr := reflect.New(t)
 	if reflect.ValueOf(v).Type().AssignableTo(t) {
 		newValuePtr.Elem().Set(reflect.ValueOf(v)) //can assign as is
-	} else {
-		//needs conversion
-		s, ok := v.(string)
-		if !ok {
-			jsonValue, _ := json.Marshal(v)
-			s = string(jsonValue)
-		}
-		//is string value, unmarshal as quoted or unquoted JSON value
-		if err := json.Unmarshal([]byte("\""+s+"\""), newValuePtr.Interface()); err != nil {
-			if err := json.Unmarshal([]byte(s), newValuePtr.Interface()); err != nil {
-				return newValuePtr.Elem(), errors.Wrapf(err, "invalid \"%s\"", s)
-			}
+		return newValuePtr.Elem(), nil
+	}
+
+	//needs conversion
+	s, ok := v.(string)
+	if !ok {
+		jsonValue, _ := json.Marshal(v)
+		s = string(jsonValue)
+	}
+
+	//now we have string value
+	if valueScanner, ok := newValuePtr.Interface().(sql.Scanner); ok {
+		//if has scanner - prefer that over json unmarshal
+		//because we do not know if json expects quoted/unquoted for this type
+		//and if we try quoted, it fail, then try unquoted, we have two different
+		//errors one one of them will be kind of meaning nothing, but which?
+		//scanner should always take the value as typed, so let's try that first
+		if err := valueScanner.Scan(s); err == nil {
+			return newValuePtr.Elem(), nil
 		}
 	}
+
+	//try JSON unmarshal as is else with quotes
+	if err := json.Unmarshal([]byte(s), newValuePtr.Interface()); err == nil {
+		return newValuePtr.Elem(), nil
+	}
+	if err := json.Unmarshal([]byte("\""+s+"\""), newValuePtr.Interface()); err != nil {
+		return newValuePtr.Elem(), errors.Wrapf(err, "invalid \"%s\"", s)
+	}
 	return newValuePtr.Elem(), nil
 }
-- 
GitLab