如何将Ballerina原生`json`转为`java.lang.Object`以调用绑定Java方法?
我有一个接收Ballerina原生json对象的函数,希望将其传递给已用bal bindgen生成Ballerina绑定的Java函数ObjectMapper.writeValueAsBytes(该函数接收java.lang.Object类型参数)。请问如何实现从json到java.lang.Object的转换?也就是下面代码中的mysteryFunc应该是什么?
function serialize(json input) returns byte[]|jackson2:JsonProcessingException|error { msgpack:MessagePackMapper mapper = msgpack:newMessagePackMapper1(); javalang:Object java_obj = mysteryFunc(input); return mapper.writeValueAsBytes(java_obj); }
生成MessagePackMapper类的bindgen命令:
bal bindgen -mvn org.msgpack:jackson-dataformat-msgpack:0.9.3 org.msgpack.jackson.dataformat.MessagePackMapper
我尝试了以下代码,能编译但运行时抛出Java错误:
import ballerina/io; import ballerina/websocket; import ballerina/jballerina.java; function createJsonHandle(json data) returns handle = @java:Method { name: "createHandleValue", 'class: "io.ballerina.runtime.api.creators.ValueCreator" } external; function createMsgpackMapperHandle() returns handle = @java:Constructor { 'class: "org.msgpack.jackson.dataformat.MessagePackMapper" } external; function writeValueAsBytes(handle mapperObj, handle input) returns handle|error = @java:Method { 'class: "org.msgpack.jackson.dataformat.MessagePackMapper" } external; function encode(json input) returns byte[] | error { handle mapper = createMsgpackMapperHandle(); handle input_handle = createJsonHandle(input); handle value = check writeValueAsBytes(mapper, input_handle); string? str_value = java:toString(value); return string:toBytes(str_value ?: ""); }
错误信息:
error: com.fasterxml.jackson.databind.JsonMappingException { "message":"iterator cannot be immutable (through reference chain: io.ballerina.runtime.internal.values.MapValueImpl[\"method\"]-> io.ballerina.runtime.internal.values.BmpStringValue[\"iterator\"]-> io.ballerina.runtime.internal.values.CharIterator[\"type\"]-> io.ballerina.runtime.internal.types.BIteratorType[\"immutableType\"] )", "cause":error( "io.ballerina.runtime.internal.util.exceptions.BallerinaException", message="iterator cannot be immutable" )}
问题根源在于直接将Ballerina内部的json对象句柄传递给Jackson时,Jackson无法识别Ballerina特有的不可变迭代器类型,导致序列化失败。正确的做法是通过JSON字符串中转,将Ballerina原生json转换为Jackson可处理的Java类型。
方法一:直接序列化JSON字符串
先把Ballerina json转为标准JSON字符串,再将字符串转为Java String对象传递给writeValueAsBytes:
import ballerina/jballerina.java; import ballerina/json; function serialize(json input) returns byte[]|error { // 将Ballerina json转为JSON字符串 string jsonStr = check json:toString(input); // 转换为Java String对象 javalang:Object javaStr = java:fromString(jsonStr); msgpack:MessagePackMapper mapper = msgpack:newMessagePackMapper1(); return mapper.writeValueAsBytes(javaStr); }
方法二:先解析为通用Java对象
如果需要将JSON转为通用Java对象(如Map/List)再序列化,可以借助Jackson的ObjectMapper先解析字符串:
import ballerina/jballerina.java; import ballerina/json; // 绑定Jackson的ObjectMapper构造方法 function createObjectMapper() returns javalang:Object = @java:Constructor { 'class: "com.fasterxml.jackson.databind.ObjectMapper" } external; // 绑定ObjectMapper的readValue方法,用于将字符串解析为Java对象 function readValue(javalang:Object mapper, string jsonStr, javalang:Object targetClass) returns javalang:Object|error = @java:Method { 'class: "com.fasterxml.jackson.databind.ObjectMapper", params: ["java.lang.String", "java.lang.Class"] } external; function serialize(json input) returns byte[]|error { string jsonStr = check json:toString(input); // 创建Jackson ObjectMapper实例 javalang:Object jacksonMapper = createObjectMapper(); // 将JSON字符串解析为通用Java对象 javalang:Object javaObj = check readValue(jacksonMapper, jsonStr, java:classObject("java.lang.Object")); msgpack:MessagePackMapper mapper = msgpack:newMessagePackMapper1(); return mapper.writeValueAsBytes(javaObj); }
失败原因说明
之前用createHandleValue获取的是Ballerina runtime内部的json实现对象(如MapValueImpl),这类对象包含Ballerina特有的不可变迭代器类型,Jackson没有对应的序列化逻辑,因此抛出iterator cannot be immutable异常。通过JSON字符串中转可以完全规避Ballerina内部类型的兼容性问题。
内容的提问来源于stack exchange,提问作者DrewDiezel

