英文:
Converting unknown Java types to Rust types
问题
我正在尝试将Java中的HashMap传递到Rust,并将其转换为内部对象,以匹配数据类型等。Java中的HashMap是HashMap<String,Object>
,其中的Object
可以是任何数据类型,包括另一个HashMap<String,Object>
。
我对JObject
的类型有些困惑,不知道如何将JObject
转换为类似于JString
的东西。
如你所见,我在这方面几乎没有进展,但作为第一步,我想要返回String
、Integer
或Date
的字符串值(其中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_local
将JValue
转换为JObject
,这样我就可以再次对其调用方法?
如果我应该使用基本类型jchar
来获取toString
的值,我如何将其转换为&str
或String
?
编辑:
在 @Chris Jester-Young 的帮助下,我已经成功创建了jobj_to_string
和jobj_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<String, Object>
where the Object
could be any data type including another HashMap<String, Object>
.
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: &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();
// Got a JValue- now what?
}
if env.is_instance_of(obj, INTEGER_CLASS).unwrap() {
let ret2 = env.call_method(obj, "toString", "()Ljava/lang/String;", &[]).unwrap();
// Got a JValue- now what?
}
if env.is_instance_of(obj, INTEGER_CLASS).unwrap() {
let ret2 = env.call_method(obj, "getTime", "()Ljava/lang/String;", &[]).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.
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("Couldn't get java string").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 &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: &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
}
答案1
得分: 6
在jni
库中,JObject
和JString
只是JNI的jobject
和jstring
的包装器。您可以使用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: &JNIEnv, obj: JObject<'_>) -> Result<String> {
let result = env.call_method(obj, "toString", "()Ljava/lang/String;", &[])?.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: &JNIEnv, obj: JObject<'_>) -> Result<String> {
let result = env.call_method(obj, "toString", "()Ljava/lang/String;", &[])?.l()?;
Ok(env.get_string(result.into())?.into())
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论