graalvm js 與java 類型轉換的一些方法


在基於跨不同語言的通信開發中,數據類型的轉換是一個很大的部分,目前來說graalvm 對於java host 類型與js 對象類似的轉換還是不夠好
java host 對象到js 的操作我們可以通過ProxyObject 以及提供的HostAccess 注解處理

參考模式

  • js 到java 對象轉換
    這個對應的數據類型還是比較多的,比如普通類型以及java 的pojo 類型
    簡單類型,graalvm 已經提供了as 方法,但是不是很好的地方是當前不支持直接到pojo,我們需要轉換為其他支持的類型再處理,一般我們
    日常會使用到的類型有array,string,int,object 。。。。,為了方便我們可以結合 jackson-databind 來處理類型轉換
    pojo 的我們解決map 類型到pojo 的轉換處理
    參考代碼
 
Value execResult =result.execute(ProxyObject.fromMap(ob),filed);
Map<String, Object> results = execResult.as(Map.class);
System.out.println("demoappa"+execResult.as(List.class));
ObjectMapper objectMapper = new ObjectMapper();
CollectionType javaType = objectMapper.getTypeFactory()
    .constructCollectionType(List.class, MyUser.class);
List myusers = execResult.as(List.class);
System.out.println("myusers"+myusers);
ArrayList<MyUser> users = objectMapper.convertValue(myusers, javaType);
System.out.println("dalong"+users);

Array 類型的轉換:
通過Value 提供的as List 轉換

 
Value execResult =result.execute(ProxyObject.fromMap(ob),filed);
Map<String, Object> results = execResult.as(Map.class);
ObjectMapper objectMapper = new ObjectMapper();
CollectionType javaType = objectMapper.getTypeFactory()
    .constructCollectionType(List.class, MyUser.class);
List<? extends Object> myusers = execResult.as(List.class);
System.out.println("myusers---"+(List<MyUser>)myusers);
ArrayList<MyUser> users = objectMapper.convertValue(myusers, javaType);
System.out.println("dalong"+users);

目前內部轉換的支持代碼(后期可能會變)

 @TruffleBoundary
    private static <T> T asJavaObject(Object value, Class<T> targetType, Type genericType, boolean allowsImplementation, PolyglotLanguageContext languageContext) {
        Objects.requireNonNull(value);
        InteropLibrary interop = InteropLibrary.getFactory().getUncached(value);
        Object obj;
        if (interop.isNull(value)) {
            if (targetType.isPrimitive()) {
                throw HostInteropErrors.nullCoercion(languageContext, value, targetType);
            }
            return null;
        } else if (HostObject.isJavaInstance(targetType, value)) {
            obj = HostObject.valueOf(value);
        } else if (targetType == Object.class) {
            obj = convertToObject(value, languageContext, interop);
        } else if (targetType == List.class) {
            if (interop.hasArrayElements(value)) {
                boolean implementsFunction = shouldImplementFunction(value, interop);
                TypeAndClass<?> elementType = getGenericParameterType(genericType, 0);
                obj = PolyglotList.create(languageContext, value, implementsFunction, elementType.clazz, elementType.type);
            } else {
                throw HostInteropErrors.cannotConvert(languageContext, value, targetType, "Value must have array elements.");
            }
        } else if (targetType == Map.class) {
            Class<?> keyClazz = getGenericParameterType(genericType, 0).clazz;
            TypeAndClass<?> valueType = getGenericParameterType(genericType, 1);
            if (!isSupportedMapKeyType(keyClazz)) {
                throw newInvalidKeyTypeException(keyClazz);
            }
            boolean hasSize = (Number.class.isAssignableFrom(keyClazz)) && interop.hasArrayElements(value);
            boolean hasKeys = (keyClazz == Object.class || keyClazz == String.class) && interop.hasMembers(value);
            if (hasKeys || hasSize) {
                boolean implementsFunction = shouldImplementFunction(value, interop);
                obj = PolyglotMap.create(languageContext, value, implementsFunction, keyClazz, valueType.clazz, valueType.type);
            } else {
                throw HostInteropErrors.cannotConvert(languageContext, value, targetType, "Value must have members or array elements.");
            }
        } else if (targetType == Function.class) {
            TypeAndClass<?> returnType = getGenericParameterType(genericType, 1);
            if (interop.isExecutable(value) || interop.isInstantiable(value)) {
                obj = PolyglotFunction.create(languageContext, value, returnType.clazz, returnType.type);
            } else if (interop.hasMembers(value)) {
                obj = HostInteropReflect.newProxyInstance(targetType, value, languageContext);
            } else {
                throw HostInteropErrors.cannotConvert(languageContext, value, targetType, "Value must be executable or instantiable.");
            }
        } else if (targetType.isArray()) {
            if (interop.hasArrayElements(value)) {
                obj = truffleObjectToArray(interop, value, targetType, genericType, languageContext);
            } else {
                throw HostInteropErrors.cannotConvert(languageContext, value, targetType, "Value must have array elements.");
            }
        } else if (targetType == LocalDate.class) {
            if (interop.isDate(value)) {
                try {
                    obj = interop.asDate(value);
                } catch (UnsupportedMessageException e) {
                    throw shouldNotReachHere(e);
                }
            } else {
                throw HostInteropErrors.cannotConvert(languageContext, value, targetType, "Value must have date and time information.");
            }
        } else if (targetType == LocalTime.class) {
            if (interop.isTime(value)) {
                try {
                    obj = interop.asTime(value);
                } catch (UnsupportedMessageException e) {
                    throw shouldNotReachHere(e);
                }
            } else {
                throw HostInteropErrors.cannotConvert(languageContext, value, targetType, "Value must have date and time information.");
            }
        } else if (targetType == LocalDateTime.class) {
            if (interop.isDate(value) && interop.isTime(value)) {
                LocalDate date;
                LocalTime time;
                try {
                    date = interop.asDate(value);
                    time = interop.asTime(value);
                } catch (UnsupportedMessageException e) {
                    throw shouldNotReachHere(e);
                }
                obj = createDateTime(date, time);
            } else {
                throw HostInteropErrors.cannotConvert(languageContext, value, targetType, "Value must have date and time information.");
            }
        } else if (targetType == ZonedDateTime.class) {
            if (interop.isDate(value) && interop.isTime(value) && interop.isTimeZone(value)) {
                LocalDate date;
                LocalTime time;
                ZoneId timeZone;
                try {
                    date = interop.asDate(value);
                    time = interop.asTime(value);
                    timeZone = interop.asTimeZone(value);
                } catch (UnsupportedMessageException e) {
                    throw shouldNotReachHere(e);
                }
                obj = createZonedDateTime(date, time, timeZone);
            } else {
                throw HostInteropErrors.cannotConvert(languageContext, value, targetType, "Value must have date, time and time-zone information.");
            }
        } else if (targetType == ZoneId.class) {
            if (interop.isTimeZone(value)) {
                try {
                    obj = interop.asTimeZone(value);
                } catch (UnsupportedMessageException e) {
                    throw shouldNotReachHere(e);
                }
            } else {
                throw HostInteropErrors.cannotConvert(languageContext, value, targetType, "Value must have time-zone information.");
            }
        } else if (targetType == Instant.class || targetType == Date.class) {
            if (interop.isDate(value) && interop.isTime(value) && interop.isTimeZone(value)) {
                Instant instantValue;
                try {
                    instantValue = interop.asInstant(value);
                } catch (UnsupportedMessageException e) {
                    throw shouldNotReachHere(e);
                }
                if (targetType == Date.class) {
                    obj = Date.from(instantValue);
                } else {
                    obj = targetType.cast(instantValue);
                }
            } else {
                throw HostInteropErrors.cannotConvert(languageContext, value, targetType, "Value must have date, time and time-zone information.");
            }
        } else if (targetType == Duration.class) {
            if (interop.isDuration(value)) {
                try {
                    obj = interop.asDuration(value);
                } catch (UnsupportedMessageException e) {
                    throw shouldNotReachHere(e);
                }
            } else {
                throw HostInteropErrors.cannotConvert(languageContext, value, targetType, "Value must have duration information.");
            }
        } else if (targetType == PolyglotException.class) {
            if (interop.isException(value)) {
                obj = asPolyglotException(value, interop, languageContext);
            } else {
                throw HostInteropErrors.cannotConvert(languageContext, value, targetType, "Value must be an exception.");
            }
        } else if (allowsImplementation && targetType.isInterface()) {
            if (HostInteropReflect.isFunctionalInterface(targetType) && (interop.isExecutable(value) || interop.isInstantiable(value))) {
                obj = HostInteropReflect.asJavaFunction(targetType, value, languageContext);
            } else if (interop.hasMembers(value)) {
                obj = HostInteropReflect.newProxyInstance(targetType, value, languageContext);
            } else {
                throw HostInteropErrors.cannotConvert(languageContext, value, targetType, "Value must have members.");
            }
        } else {
            throw HostInteropErrors.cannotConvert(languageContext, value, targetType, "Unsupported target type.");
        }
        assert targetType.isInstance(obj);
        return targetType.cast(obj);
    }
  • java host object && meethod 2 js
    對於object 類型,比較推薦的是通過ProxyObject 進行map轉換處理(對應到js 的object)
    java 代碼:
 
Map<String, Object> ob =new HashMap<>();
Map<String, Object> ob2 =new HashMap<>();
ob2.put("url","https://plus.google.com/102817283354809142195/posts/F97fqZwJESL");
ob.put("url","https://plus.google.com/102817283354809142195/posts/F97fqZwJESL");
Map<String, Object> ob3 =new HashMap<>();
ob3.put("url","https://plus.google.com/102817283354809142195/posts/F97fqZwJESL");
ob2.put("ob",ProxyObject.fromMap(ob3));
ob.put("id","z12gtjhq3qn2xxl2o224exwiqruvtda0i");
ob.put("object",ProxyObject.fromMap(ob2));

js 代碼:

window = this || {}
window.demo = function (datas, fields) {
    let mydatas 
    mydatas = datas;
    if (typeof datas === 'string') {
        mydatas =JSON.parse(datas)
    }
    return mask(mydatas, fields)
}

普通java對象通過 @HostAccess 注解暴露

public class Dalong {
    @HostAccess.Export
    public   String  username;
    @HostAccess.Export
    public   String  password;
    @HostAccess.Export
    public  String token() {
        return String.format("%s------%s",this.username,this.password);
    }
}

使用

Dalong dalong = new Dalong();
dalong.password="dalong";
dalong.username="dalong";
Context context = Context.newBuilder().allowAllAccess(true).allowHostClassLoading(true).allowHostAccess(hostAccess).allowIO(true).allowNativeAccess(true).engine(engine).build();
context.getBindings("js").putMember("mydalong",dalong);
System.out.println(context.getBindings("js").getMemberKeys());
String myjs = "mydalong.username= \"dalongdemossss\";\n" +
        "mydalong.password= \"deeeeeeee\";\n" +
        "\n" +
        "console.log(\"my token\",mydalong.token())\n" +
        "\n";
context.eval("js",myjs);

說明

以上是一些自己簡單的整理,實際應該還會有其他更方便的方法,graalvm 的js 能力很強大,很值得使用

參考資料

https://www.graalvm.org/sdk/javadoc/org/graalvm/polyglot/Value.html
https://github.com/AMIS-Services/jfall2019-graalvm/tree/master/polyglot/java2js
https://github.com/graalvm/graaljs/issues/94


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM