From 0e5800687f670f69160d37c232b6137a45589b5c Mon Sep 17 00:00:00 2001 From: ChenYaoGang Date: Thu, 14 Sep 2023 12:56:17 +0800 Subject: [PATCH] fix: Convert PyList to JArray (or PyDict to JObject) to solve the problem of `True` (or `False`) in python causing conversion failure. ```c# PyObject pyobj; //python dict: {"test": True} var obj = Newtonsoft.Json.JsonConvert.DeserializeObject(pyobj.ToString(), typeof(JObject)); // cause error: Unexpected character encountered while parsing value: T. Path 'test', line 1, position 9. ``` --- OpenRPA.Script/Activities/InvokeCode.cs | 52 +--- OpenRPA.Script/Activities/ParseUtils.cs | 282 ++++++++++++++++++ OpenRPA.Script/Activities/ScriptActivities.cs | 71 +---- 3 files changed, 287 insertions(+), 118 deletions(-) create mode 100644 OpenRPA.Script/Activities/ParseUtils.cs diff --git a/OpenRPA.Script/Activities/InvokeCode.cs b/OpenRPA.Script/Activities/InvokeCode.cs index 66bf43aa..b16d2be8 100644 --- a/OpenRPA.Script/Activities/InvokeCode.cs +++ b/OpenRPA.Script/Activities/InvokeCode.cs @@ -471,57 +471,13 @@ public void Execute(CodeActivityContext context, string code, string language, D } else if (a.Value.ArgumentType == typeof(object)) { - if (PyString.IsStringType(pyobj)) - { - Arguments[a.Key].Set(context, pyobj.ToString()); - } - else if (PyNumber.IsNumberType(pyobj) && ("True" == pyobj.ToString() || "False" == pyobj.ToString())) - { - Arguments[a.Key].Set(context, bool.Parse(pyobj.ToString())); - } - else if (PyInt.IsIntType(pyobj)) - { - Arguments[a.Key].Set(context, int.Parse(pyobj.ToString())); - } - else if (PyFloat.IsFloatType(pyobj)) - { - Arguments[a.Key].Set(context, float.Parse(pyobj.ToString())); - } - else if (PyDict.IsDictType(pyobj)) - { - try - { - var obj = Newtonsoft.Json.JsonConvert.DeserializeObject(pyobj.ToString(), typeof(JObject)); - Arguments[a.Key].Set(context, obj); - } - catch (Exception _ex) - { - Log.Information("Failed variable " + parameter.Key + " of type " + a.Value.ArgumentType.FullName + " " + _ex.Message); - } - } - else if (PyList.IsListType(pyobj)) + try { - try - { - var obj = Newtonsoft.Json.JsonConvert.DeserializeObject(pyobj.ToString(), typeof(JArray)); - Arguments[a.Key].Set(context, obj); - } - catch (Exception _ex) - { - Log.Information("Failed variable " + parameter.Key + " of type " + a.Value.ArgumentType.FullName + " " + _ex.Message); - } + Arguments[a.Key].Set(context, ParseUtils.PyObjectToObjectForObjectArgument(pyobj)); } - else + catch (Exception _ex) { - try - { - var obj = Newtonsoft.Json.JsonConvert.DeserializeObject(pyobj.ToString(), a.Value.ArgumentType); - Arguments[a.Key].Set(context, obj); - } - catch (Exception _ex) - { - Log.Information("Failed variable " + parameter.Key + " of type " + a.Value.ArgumentType.FullName + " " + _ex.Message); - } + Log.Information("Failed variable " + parameter.Key + " of type " + a.Value.ArgumentType.FullName + " " + _ex.Message); } } else diff --git a/OpenRPA.Script/Activities/ParseUtils.cs b/OpenRPA.Script/Activities/ParseUtils.cs new file mode 100644 index 00000000..4520ec1e --- /dev/null +++ b/OpenRPA.Script/Activities/ParseUtils.cs @@ -0,0 +1,282 @@ +using Newtonsoft.Json.Linq; +using Python.Runtime; +using System; +using System.Collections.Generic; + +using OpenRPA.Interfaces; + +namespace OpenRPA.Script.Activities +{ + public class ParseUtils + { + public static object PyObjectToObjectForObjectArgumentWithGIL(PyObject pyobj) + { + try + { + InvokeCode.InitPython(); + using (Py.GIL()) + { + return PyObjectToObjectForObjectArgument(pyobj); + } + } + catch (Exception ex) + { + Log.Warning(ex.ToString()); + throw new Exception("Failed for 'PyObjectToObjectForObjectArgument': " + ex.ToString()); + } + } + + public static object PyObjectToObjectForObjectArgument(PyObject pyobj) + { + object obj; + if (pyobj.IsNone()) + { + obj = null; + } + else if (PyString.IsStringType(pyobj)) + { + obj = pyobj.ToString(); + } + else if (PyNumber.IsNumberType(pyobj) && ("True" == pyobj.ToString() || "False" == pyobj.ToString())) + { + obj = bool.Parse(pyobj.ToString()); + } + else if (PyInt.IsIntType(pyobj)) + { + obj = int.Parse(pyobj.ToString()); + } + else if (PyFloat.IsFloatType(pyobj)) + { + obj = float.Parse(pyobj.ToString()); + } + else if (PyDict.IsDictType(pyobj)) + { + obj = PyDictToJObject(new PyDict(pyobj)); + } + else if (PyList.IsListType(pyobj)) + { + obj = PyListToJArray(new PyList(pyobj)); + } + else + { + obj = Newtonsoft.Json.JsonConvert.DeserializeObject(pyobj.ToString(), typeof(object)); + } + + return obj; + } + + + public static JArray PyListToJArrayWithGIL(PyList list) + { + try + { + InvokeCode.InitPython(); + using (Py.GIL()) + { + return PyListToJArray(list); + } + } + catch (Exception ex) + { + Log.Warning(ex.ToString()); + throw new Exception("Failed for 'PyListToJArray': " + ex.ToString()); + } + } + + /// + /// Convert PyList to JArray to solve the problem of `True` (or `False`) in python causing conversion failure + /// + /// + /// + public static JArray PyListToJArray(PyList list) + { + JArray jarr = new JArray(); + foreach (PyObject pyobj in list) + { + JToken value; + if (pyobj.IsNone()) + { + value = null; + } + else if (PyString.IsStringType(pyobj)) + { + value = pyobj.ToString(); + } + else if (PyNumber.IsNumberType(pyobj) && ("True" == pyobj.ToString() || "False" == pyobj.ToString())) + { + value = bool.Parse(pyobj.ToString()); + } + else if (PyInt.IsIntType(pyobj)) + { + value = int.Parse(pyobj.ToString()); + } + else if (PyFloat.IsFloatType(pyobj)) + { + value = float.Parse(pyobj.ToString()); + } + else if (PyDict.IsDictType(pyobj)) + { + value = PyDictToJObject(new PyDict(pyobj)); + } + else if (PyList.IsListType(pyobj)) + { + value = PyListToJArray(new PyList(pyobj)); + } + else + { // ? process other types + value = pyobj.ToString(); + } + + jarr.Add(value); + } + + return jarr; + } + + public static JObject PyDictToJObjectWithGIL(PyDict dict) + { + try + { + InvokeCode.InitPython(); + using (Py.GIL()) + { + return PyDictToJObject(dict); + } + } + catch (Exception ex) + { + Log.Warning(ex.ToString()); + throw new Exception("Failed for 'PyDictToJObject': " + ex.ToString()); + } + } + + + /// + /// Convert PyDict to JObject to solve the problem of `True` (or `False`) in python causing conversion failure + /// + /// + /// + public static JObject PyDictToJObject(PyDict dict) + { + JObject jobj = new JObject(); + foreach(var key in dict.Keys()) + { + PyObject pyobj = dict.GetItem(key); + JToken value; + if (pyobj.IsNone()) + { + value = null; + } + else if (PyString.IsStringType(pyobj)) + { + value = pyobj.ToString(); + } + else if (PyNumber.IsNumberType(pyobj) && ("True" == pyobj.ToString() || "False" == pyobj.ToString())) + { + value = bool.Parse(pyobj.ToString()); + } + else if (PyInt.IsIntType(pyobj)) + { + value = int.Parse(pyobj.ToString()); + } + else if (PyFloat.IsFloatType(pyobj)) + { + value = float.Parse(pyobj.ToString()); + } + else if (PyDict.IsDictType(pyobj)) + { + value = PyDictToJObject(new PyDict(pyobj)); + } + else if (PyList.IsListType(pyobj)) + { + value = PyListToJArray(new PyList(pyobj)); + } + else + { // ? process other types + value = pyobj.ToString(); + } + + jobj.Add(key.ToString(), value); + } + + return jobj; + } + + public static PyObject ToPyObjectWithGIL(object obj) + { + try + { + InvokeCode.InitPython(); + using (Py.GIL()) + { + return ToPyObject(obj); + } + } + catch (Exception ex) + { + Log.Warning(ex.ToString()); + throw new Exception("Failed for 'ToPyObject': " + ex.ToString()); + } + } + + /// + /// Convert objects in c# to PyObject (such as list or dict) to facilitate data access in python + /// + /// + /// + public static PyObject ToPyObject(object obj) + { + if (obj == null) + { + return Runtime.None; + } + else if (obj is JObject) + { + PyDict pyDict = new PyDict(); + foreach (var item in (JObject)obj) + { + pyDict.SetItem(item.Key, ToPyObject(item.Value)); + } + return pyDict; + } + else if (obj is JArray) + { + PyList pyList = new PyList(); + foreach (var item in (JArray)obj) + { + pyList.Append(ToPyObject(item)); + } + return pyList; + } + else if (obj is JValue) + { + object value = ((JValue)obj).Value; + return ToPyObject(value); + } + else if (ScriptActivities.IsDictionary(obj, typeof(string))) + { + IDictionary dict = (IDictionary)obj; + PyDict pyDict = new PyDict(); + foreach (var item in dict) + { + pyDict.SetItem(item.Key, ToPyObject(item.Value)); + } + return pyDict; + } + else if (ScriptActivities.IsList(obj, null)) + { + IList list = (IList)obj; + PyList pyList = new PyList(); + foreach (var item in list) + { + pyList.Append(ToPyObject(item)); + } + return pyList; + } + else + { + return obj.ToPython(); + } + } + } +} diff --git a/OpenRPA.Script/Activities/ScriptActivities.cs b/OpenRPA.Script/Activities/ScriptActivities.cs index ba84bdd8..496be977 100644 --- a/OpenRPA.Script/Activities/ScriptActivities.cs +++ b/OpenRPA.Script/Activities/ScriptActivities.cs @@ -5,14 +5,12 @@ using System.Reflection.Emit; using System.Reflection; - using OpenRPA.Interfaces; using System.Activities; using System.Collections.ObjectModel; using System.IO; using Newtonsoft.Json.Linq; using System.ComponentModel; -using Python.Runtime; using System.Collections.Concurrent; using System.Text; using System.Drawing; @@ -462,61 +460,6 @@ public InternalInvokeCode() designerIconFile = scriptItem.DesignerIconFile; } - - private PyObject ToPythonObject(object obj) - { - if (obj == null) - { - return null; - }else if (obj is JObject) - { - PyDict pyDict = new PyDict(); - foreach (var item in (JObject)obj) - { - pyDict.SetItem(item.Key, ToPythonObject(item.Value)); - } - return pyDict; - } - else if (obj is JArray) - { - PyList pyList = new PyList(); - foreach (var item in (JArray)obj) - { - pyList.Append(ToPythonObject(item)); - } - return pyList; - } - else if (obj is JValue) - { - object value = ((JValue)obj).Value; - return ToPythonObject(value); - } - else if (ScriptActivities.IsDictionary(obj, typeof(string))) - { - IDictionary dict = (IDictionary)obj; - PyDict pyDict = new PyDict(); - foreach (var item in dict) - { - pyDict.SetItem(item.Key, ToPythonObject(item.Value)); - } - return pyDict; - } - else if (ScriptActivities.IsList(obj, null)) - { - IList list = (IList)obj; - PyList pyList = new PyList(); - foreach (var item in list) - { - pyList.Append(ToPythonObject(item)); - } - return pyList; - } - else - { - return obj.ToPython(); - } - } - protected override void Execute(CodeActivityContext context) { ScriptItem item = ScriptItemDict[TypeKey]; @@ -527,19 +470,7 @@ protected override void Execute(CodeActivityContext context) if (language == "Python") { - try - { - InvokeCode.InitPython(); - using (Python.Runtime.Py.GIL()) - { - args = ToPythonObject(args); - } - } - catch (Exception ex) - { - Log.Warning(ex.ToString()); - throw new Exception("Failed for 'ToPythonObject': " + ex.ToString()); - } + args = ParseUtils.ToPyObjectWithGIL(args); } rpa_args.Set(context, args);