如何解决从BLOB变量读取Unicode JSON数据的乱码问题?
解决BLOB中Unicode JSON读取乱码的问题
这个问题我之前处理过,核心原因是你把NVARCHAR2字符串转成BLOB时用错了方法,导致生成的RAW数据字符集不匹配,最终json_query解析时出现乱码。
问题根源
你用了UTL_RAW.cast_to_raw来转换包含Unicode字符的NVARCHAR2字符串:
lv_json_tx := UTL_RAW.cast_to_raw (n'{"text":"' || lv_value_tx || '"}');
这个函数的问题在于:它会先把NVARCHAR2类型的字符串转换为数据库默认的VARCHAR2字符集(如果你的数据库字符集不支持这些西里尔字符,转换过程中就会丢失字符信息),然后再转成RAW。这样生成的BLOB实际上已经不是正确的Unicode编码了,后续json_query默认按数据库字符集解析,自然会出现乱码。
简便解决方案
我们需要用指定字符集的转换函数来生成正确的Unicode格式BLOB,同时在解析JSON时明确告诉Oracle使用的字符集。具体步骤如下:
- 用
UTL_I18N.STRING_TO_RAW替代UTL_RAW.cast_to_raw,指定字符集为AL16UTF16(Oracle中NVARCHAR2默认使用的Unicode编码)。 - 在
json_query中通过CHARSET AL16UTF16参数指定解析BLOB时使用的字符集。
修改后的完整代码
CREATE TABLE json_test ( step_no INTEGER, txt NVARCHAR2 (2000) ); CREATE TABLE nchar_text ( txt NVARCHAR2 (2000) ); INSERT INTO nchar_text (txt) VALUES ('Опасен за околната среда.'); COMMIT; DECLARE lv_json_tx BLOB; lv_value_tx NVARCHAR2 (2000); lv_vu NVARCHAR2 (2000); BEGIN SELECT txt INTO lv_value_tx FROM nchar_text WHERE ROWNUM <= 1; INSERT INTO json_test VALUES (1, lv_value_tx); -- 改用UTL_I18N.STRING_TO_RAW指定AL16UTF16字符集 lv_json_tx := UTL_I18N.STRING_TO_RAW(n'{"text":"' || lv_value_tx || '"}', 'AL16UTF16'); INSERT INTO json_test VALUES (2, UTL_RAW.cast_to_nvarchar2 (lv_json_tx)); -- json_query中指定CHARSET AL16UTF16 SELECT json_query (lv_json_tx FORMAT JSON CHARSET AL16UTF16, '$.text' WITH WRAPPER) AS text INTO lv_vu FROM DUAL; INSERT INTO json_test VALUES (3, lv_vu); END; /
执行结果验证
执行修改后的代码后,查询json_test表会得到正确的结果:
STEP_NO TXT 1 Опасен за околната среда. 2 {"text":"Опасен за околната среда."} 3 ["Опасен за околната среда."]
步骤3的结果已经正常显示Unicode字符,没有乱码。
额外说明
如果你的场景中需要用UTF-8格式存储JSON(更通用的Unicode格式),也可以将NVARCHAR2转成UTF-8编码的BLOB:
lv_json_tx := UTL_I18N.STRING_TO_RAW(n'{"text":"' || lv_value_tx || '"}', 'UTF8');
此时json_query需要指定CHARSET UTF8,同样能得到正确结果。
内容的提问来源于stack exchange,提问作者Scott K




