如何防止 Snowflake Python 连接器在 ARRAY/OBJECT/VARIANT 类型列上进行 json 格式设置?
Posted
技术标签:
【中文标题】如何防止 Snowflake Python 连接器在 ARRAY/OBJECT/VARIANT 类型列上进行 json 格式设置?【英文标题】:How can you prevent the Snowflake Python connector from doing json formatting on ARRAY/OBJECT/VARIANT type columns? 【发布时间】:2020-07-20 16:59:39 【问题描述】:我正在开发一项服务,该服务从 Snowflake 中提取 VARIANT 数据并公开以供在其他地方使用,并且我想防止 Snowflake 连接器转义和添加字符串格式。我正在尝试的一种方法是使用here 提到的converter_class
选项。
再深入一点,我发现默认的SnowflakeConverter
类根本没有对VARIANT/ARRAY/OBJECT 转换执行任何操作:https://github.com/snowflakedb/snowflake-connector-python/blob/79a106ba70355249ea0eff16977bafe774846d90/converter.py#L315。既然是这样,我扩展了这个类以将其用作 _ARRAY_to_python 转换器(首先使用 ARRAY):
def convert_array_to_python(self, ctx):
logger.info('trying to convert')
return lambda x: [s.strip('\"\n\t ') for s in x.strip('[\n\t ]').split(',')]
并使用了conn = connect(user=...., converter_class=CustomConverter())
,其中CustomConverter
看起来像这样,并且做了类似于DefaultConverterClass
函数here:
def CustomConverterClass():
converter = sf_lib.connection.DefaultConverterClass()
converter._ARRAY_to_python = convert_array_to_python
return converter
我在建立连接后检查了conn.converter_class()._ARRAY_to_python
和getattr(conn.converter_class(), '_ARRAY_to_python')
的值,它们都与前面定义的convert_array_to_python
函数匹配。然而,当我执行查询并检查 VARIANT 列的光标时:
cursor = conn.cursor(sf_lib.DictCursor)
data = cursor.execute(query)
for row in data:
...
这些值仍然被转义。而且,它看起来不像调用了 ARRAY 转换器。我可以控制我正在查询的表的架构,并确认该列的类型为 ARRAY。
Snowflake 连接器似乎使用 JsonResult 进行 json 转换。据我所知,通过源代码挖掘,_ARRAY_to_python
调用的调用堆栈应该是这样的:
next(data)
-> JsonDictResult.__next__()
-> JsonDictResult._row_to_python(row)
-> _convert_ARRAY_to_python(col)
(来自https://github.com/snowflakedb/snowflake-connector-python/blob/d4f04f20949e2bcb70b0ea0927a8a362ba272389/json_result.py#L178)
为什么这里没有调用转换器,还有什么方法可以防止应用 json 格式?
【问题讨论】:
【参考方案1】:我想防止雪花连接器转义和添加字符串格式
其实并不是Snowflake 转义和添加字符串格式。 snowflake-connector 接收字符串,然后转换回它们的类型。对于 Variant,它不进行任何转换,所以这就是您使用字符串的原因。
回答Snowflake为什么不调用converter_class方法的问题,是因为Snowflake现在使用Apache Arrow来获取查询结果 https://www.snowflake.com/blog/fetching-query-results-from-snowflake-just-got-a-lot-faster-with-apache-arrow/?lang=fr
那么它改变了什么? _json_result_class 方法不再被调用 https://github.com/snowflakedb/snowflake-connector-python/blob/79a106ba70355249ea0eff16977bafe774846d90/cursor.py#L624
if self._query_result_format == 'arrow':
self.check_can_use_arrow_resultset()
self._result = ArrowResult(data, self, use_dict_result=self._use_dict_result)
else:
self._result = self._json_result_class(data, self)
实际上正是这个方法使用转换器类来序列化/反序列化结果 https://github.com/snowflakedb/snowflake-connector-python/blob/79a106ba70355249ea0eff16977bafe774846d90/json_result.py#L132
row[idx] = col if conv is None or col is None else conv(col)
简而言之:converter_class 不再使用,文档不是最新的
要测试您的雪花连接器版本是否使用 Arrow,您可以这样做:
from snowflake.connector.cursor import CAN_USE_ARROW_RESULT
print(CAN_USE_ARROW_RESULT)
我没有找到合适的方法来处理 Arrow 如何反序列化结果,我想您需要在迭代结果时自己将变量列转换为 dict 或 list 来处理。
快速示例:
import json
import snowflake.connector
con = snowflake.connector.connect(
...
)
cursor = con.cursor()
data = cursor.execute("""...""")
def deserialize_sf_value(value):
if isinstance(value, str) and value[0].strip() in '[':
return json.loads(value)
return value
results = [[deserialize_sf_value(value) for value in row] for row in data]
print(results)
【讨论】:
以上是关于如何防止 Snowflake Python 连接器在 ARRAY/OBJECT/VARIANT 类型列上进行 json 格式设置?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Python(SSO 身份验证)在 Snowflake 中进行查询?
雪花 Python 连接 KeyError: 'snowflake-connector-python
AWS Lambda Snowflake Python 连接器在尝试连接时挂起
Snowflake Python Pandas 连接器 - 使用 fetch_pandas_all 的未知错误