转换未知的Java类型为Rust类型

huangapple go评论82阅读模式
英文:

Converting unknown Java types to Rust types

问题

我正在尝试将Java中的HashMap传递到Rust,并将其转换为内部对象,以匹配数据类型等。Java中的HashMap是HashMap<String,Object>,其中的Object可以是任何数据类型,包括另一个HashMap<String,Object>

我对JObject的类型有些困惑,不知道如何将JObject转换为类似于JString的东西。

如你所见,我在这方面几乎没有进展,但作为第一步,我想要返回StringIntegerDate的字符串值(其中Date将是Date.getTime()的字符串值)。

static DATE_CLASS: &str = "java/util/Date";
static INTEGER_CLASS: &str = "java/lang/Integer";
static STRING_CLASS: &str = "java/lang/String";

fn get_string_value(env: &JNIEnv, obj: JObject) -> String {
    let ret = String::new();

    if env.is_instance_of(obj, STRING_CLASS).unwrap() {
        let ret2 = env.call_method(obj, "toString", "()Ljava/lang/String;", &[]).unwrap();
        // 获得了一个 JValue- 现在怎么办?
    }

    if env.is_instance_of(obj, INTEGER_CLASS).unwrap() {
        let ret2 = env.call_method(obj, "toString", "()Ljava/lang/String;", &[]).unwrap();
        // 获得了一个 JValue- 现在怎么办?
    }

    if env.is_instance_of(obj, INTEGER_CLASS).unwrap() {
        let ret2 = env.call_method(obj, "getTime", "()Ljava/lang/String;", &[]).unwrap();
        // 获得了一个 JValue- 现在怎么办?
    }

    ret
}

我认为我应该这样做(如果我理解不对,请纠正我):通过完全限定路径检查JObject是否是这些类之一的实例。一旦我知道它们的类型,我就可以使用env.call_method调用它们的方法。结果是一个JValue,它可以返回一些基本类型。

我假设除非我期望返回另一个对象,否则应该使用这些(如下图所示)。

我假设除非我期望返回另一个对象,否则应该使用这些。

let message_ref = env.auto_local(ret2).as_obj();

然后,如果需要,我可以调用另一个方法。但据我所知,我不能将其转换为JString,如果我想的话。然而,这也是我目前所知道的从JString获得Rust字符串的唯一方法,使用:

let s: String = env.get_string(a_j_string).expect("无法获取Java字符串").into();

我的做法正确吗?

我如何将JValue转换为字符串?

我是否正确地使用auto_localJValue转换为JObject,这样我就可以再次对其调用方法?

如果我应该使用基本类型jchar来获取toString的值,我如何将其转换为&strString

编辑:

在 @Chris Jester-Young 的帮助下,我已经成功创建了jobj_to_stringjobj_to_int函数。

static INTEGER_CLASS: &str = "java/lang/Integer";
static STRING_CLASS: &str = "java/lang/String";

fn get_liquid_value(env: &JNIEnv, obj: JObject) -> LiquidValue {
    let mut value = LiquidValue::Nil;

    if env.is_instance_of(obj, STRING_CLASS).unwrap() {
        match jobj_to_string(env, obj) {
            Some(str) => {
                value = LiquidValue::Scalar(LiquidScalar::from(str));
            },
            None => {}
        }
    }

    if env.is_instance_of(obj, INTEGER_CLASS).unwrap() {
        match jobj_to_i32(env, obj) {
            Some(int) => {
                value = LiquidValue::Scalar(LiquidScalar::from(int));
            },
            None => {}
        }
    }

    value
}

fn jobj_to_string(env: &JNIEnv, obj: JObject) -> Option<String> {
    let mut result = Option::None;

    match env.call_method(obj, "toString", "()Ljava/lang/String;", &[]) {
        Result::Ok(jvalue) => {
            match jvalue.l() {
                Result::Ok(jobject) => {
                    let string = String::from(env.get_string(jobject.into()).unwrap().to_str().unwrap());
                    result = Option::Some(string);
                },
                _ => {}
            }
        },
        _ => {}
    };

    result
}

fn jobj_to_i32(env: &JNIEnv, obj: JObject) -> Option<i32> {
    let mut result = Option::None;

    match env.call_method(obj, "intValue", "()I", &[]) {
        Ok(jvalue) => {
            match jvalue.i() {
                Ok(int) => {
                    result = Option::Some(int.to_owned());
                },
                _ => {}
            }
        },
        _ => {}
    }

    result
}
英文:

I am trying to take a HashMap from Java into Rust and convert it into an internal object, matching data types and all. The HashMap in java is a HashMap&lt;String, Object&gt; where the Object could be any data type including another HashMap&lt;String, Object&gt;.

I'm having a difficult time determining what type of object a JObject is, and I am blocked on how to convert a JObject to something like a JString.

As you can see I've barely gotten anywhere with it, but as a first pass I'd just like to return the string value of either a String, Integer, or Date (where Date would be the string value of Date.getTime()).

static DATE_CLASS: &amp;str = &quot;java/util/Date&quot;;
static INTEGER_CLASS: &amp;str = &quot;java/lang/Integer&quot;;
static STRING_CLASS: &amp;str = &quot;java/lang/String&quot;;

fn get_string_value(env: &amp;JNIEnv, obj: JObject) -&gt; String {
    let ret = String::new();

    if env.is_instance_of(obj, STRING_CLASS).unwrap() {
        let ret2 = env.call_method(obj, &quot;toString&quot;, &quot;()Ljava/lang/String;&quot;, &amp;[]).unwrap();
        // Got a JValue- now what?
    }

    if env.is_instance_of(obj, INTEGER_CLASS).unwrap() {
        let ret2 = env.call_method(obj, &quot;toString&quot;, &quot;()Ljava/lang/String;&quot;, &amp;[]).unwrap();
        // Got a JValue- now what?
    }

    if env.is_instance_of(obj, INTEGER_CLASS).unwrap() {
        let ret2 = env.call_method(obj, &quot;getTime&quot;, &quot;()Ljava/lang/String;&quot;, &amp;[]).unwrap();
        // Got a JValue- now what?
    }

    ret
}

I think the way I'm supposed to do this (but please correct me if not), is to check if the JObject is an instance of any of these classes by their fully qualified paths. Once I know what type they are, I can call a method on them with env.call_method. The result is a JValue, which can return some primitive types.

转换未知的Java类型为Rust类型

I'm assuming I'm supposed to be using these unless I'm expecting another object to be returned, in which case I'd use-

let message_ref = env.auto_local(ret2).as_obj();

Then I could call another method if I needed to. But as far as I know I can't then convert that into a JString if I wanted to. However, that's also the only way I know how to get a rust string out of a JString so far, using:

let s: String = env.get_string(a_j_string).expect(&quot;Couldn&#39;t get java string&quot;).into();

Am I going about this right?

How do I convert a JValue to a String?

Am I correct that I should use auto_local to convert a JValue to a JObject, and that would allow me to call methods on it again?

If I'm supposed to use the primitive type jchar to get the value of toString- how do I convert that to &amp;str or String?

Edit:

With @Chris Jester-Young's help I was able to get both a jobj_to_string and jobj_to_int functions working.

static INTEGER_CLASS: &amp;str = &quot;java/lang/Integer&quot;;
static STRING_CLASS: &amp;str = &quot;java/lang/String&quot;;

fn get_liquid_value(env: &amp;JNIEnv, obj: JObject) -&gt; LiquidValue {
    let mut value = LiquidValue::Nil;

    if env.is_instance_of(obj, STRING_CLASS).unwrap() {
        match jobj_to_string(env, obj) {
            Some(str) =&gt; {
                value = LiquidValue::Scalar(LiquidScalar::from(str));
            },
            None =&gt; {}
        }
    }

    if env.is_instance_of(obj, INTEGER_CLASS).unwrap() {
        match jobj_to_i32(env, obj) {
            Some(int) =&gt; {
                value = LiquidValue::Scalar(LiquidScalar::from(int));
            },
            None =&gt; {}
        }
    }

    value
}

fn jobj_to_string(env: &amp;JNIEnv, obj: JObject) -&gt; Option&lt;String&gt; {
    let mut result = Option::None;

    match env.call_method(obj, &quot;toString&quot;, &quot;()Ljava/lang/String;&quot;, &amp;[]) {
        Result::Ok(jvalue) =&gt; {
            match jvalue.l() {
                Result::Ok(jobject) =&gt; {
                    let string = String::from(env.get_string(jobject.into()).unwrap().to_str().unwrap());
                    result = Option::Some(string);
                },
                _ =&gt; {}
            }
        },
        _ =&gt; {}
    };

    result
}

fn jobj_to_i32(env: &amp;JNIEnv, obj: JObject) -&gt; Option&lt;i32&gt; {
    let mut result = Option::None;

    match env.call_method(obj, &quot;intValue&quot;, &quot;()I&quot;, &amp;[]) {
        Ok(jvalue) =&gt; {
            match jvalue.i() {
                Ok(int) =&gt; {
                    result = Option::Some(int.to_owned());
                },
                _ =&gt; {}
            }
        },
        _ =&gt; {}
    }

    result
}

答案1

得分: 6

jni库中,JObjectJString只是JNI的jobjectjstring的包装器。您可以使用From trait将JObject“转换”为JString。例如,JString::from(my_jobject)my_jobject.into()。不过,如果您对对象是否实际上是字符串有任何疑问,首先必须进行JNIEnv::is_instance_of检查。

关于JValue的问题,toString的结果是一个字符串,在JVM宇宙中,字符串是对象类型,而不是原始类型。因此,您可以直接使用JValue::l访问器(从我所知,您不需要使用auto_local)。由于我们知道结果是字符串,您可以直接将结果转换为JString(如上所述),然后调用JNIEnv::get_string获取一个JavaStr对象,您可以使用From trait将其转换为Rust字符串。

将所有这些组合在一起,我们得到以下内容(尚未经过测试,但至少可以在我的环境中编译通过):

fn get_string_value(env: &amp;JNIEnv, obj: JObject&lt;&#39;_&gt;) -&gt; Result&lt;String&gt; {
    let result = env.call_method(obj, &quot;toString&quot;, &quot;()Ljava/lang/String;&quot;, &amp;[])?.l()?;
    Ok(env.get_string(result.into())?.into())
}
英文:

In the jni crate, JObject and JString are just wrappers around JNI's jobject and jstring. You can "cast" a JObject into a JString using the From trait. e.g., JString::from(my_jobject) or my_jobject.into(). You do have to do a JNIEnv::is_instance_of check first, though, if you're at all unsure if the object is actually a string.

To your question about JValue, the result of toString is a string, which in the JVM universe, is an object type, not a primitive type. So you can just use the JValue::l accessor (you don't need to use auto_local from what I can tell). Since we know the result is a string, you can just cast the result to a JString directly (as described above), and then call JNIEnv::get_string to get a JavaStr object, that you can convert to a Rust string using the From trait.

Putting it all together, we get the following (not tested, but it compiles for me at least):

fn get_string_value(env: &amp;JNIEnv, obj: JObject&lt;&#39;_&gt;) -&gt; Result&lt;String&gt; {
    let result = env.call_method(obj, &quot;toString&quot;, &quot;()Ljava/lang/String;&quot;, &amp;[])?.l()?;
    Ok(env.get_string(result.into())?.into())
}

huangapple
  • 本文由 发表于 2020年9月18日 05:35:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/63946475.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定