These are used by tests. However it could even be an idea to use
serde_json + transcoding and get rid of the C version...
Co-authored-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/util/wrapper.h | 1 +
rust/util/src/qobject/mod.rs | 17 +++++++++++++++++
2 files changed, 18 insertions(+)
diff --git a/rust/util/wrapper.h b/rust/util/wrapper.h
index 0907dd59142..c88820a5e5b 100644
--- a/rust/util/wrapper.h
+++ b/rust/util/wrapper.h
@@ -37,3 +37,4 @@ typedef enum memory_order {
#include "qobject/qobject.h"
#include "qobject/qlist.h"
#include "qobject/qdict.h"
+#include "qobject/qjson.h"
diff --git a/rust/util/src/qobject/mod.rs b/rust/util/src/qobject/mod.rs
index e896aba5f3a..292a3c9c238 100644
--- a/rust/util/src/qobject/mod.rs
+++ b/rust/util/src/qobject/mod.rs
@@ -23,6 +23,7 @@
use common::assert_field_type;
pub use deserializer::from_qobject;
pub use error::{Error, Result};
+use foreign::prelude::*;
pub use serializer::to_qobject;
use crate::bindings;
@@ -111,6 +112,22 @@ fn refcnt(&self) -> &AtomicUsize {
let qobj = self.0.get();
unsafe { AtomicUsize::from_ptr(addr_of_mut!((*qobj).base.refcnt)) }
}
+
+ pub fn to_json(&self) -> String {
+ let qobj = self.0.get();
+ unsafe {
+ let json = bindings::qobject_to_json(qobj);
+ glib_sys::g_string_free(json, glib_sys::GFALSE).into_native()
+ }
+ }
+
+ pub fn from_json(json: &str) -> std::result::Result<Self, crate::Error> {
+ let c_json = std::ffi::CString::new(json)?;
+ unsafe {
+ crate::Error::with_errp(|errp| bindings::qobject_from_json(c_json.as_ptr(), errp))
+ .map(|qobj| QObject::from_raw(qobj))
+ }
+ }
}
impl From<()> for QObject {
--
2.51.0
Paolo Bonzini <pbonzini@redhat.com> writes:
> These are used by tests. However it could even be an idea to use
> serde_json + transcoding and get rid of the C version...
Tell me more!
> Co-authored-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> rust/util/wrapper.h | 1 +
> rust/util/src/qobject/mod.rs | 17 +++++++++++++++++
> 2 files changed, 18 insertions(+)
>
> diff --git a/rust/util/wrapper.h b/rust/util/wrapper.h
> index 0907dd59142..c88820a5e5b 100644
> --- a/rust/util/wrapper.h
> +++ b/rust/util/wrapper.h
> @@ -37,3 +37,4 @@ typedef enum memory_order {
> #include "qobject/qobject.h"
> #include "qobject/qlist.h"
> #include "qobject/qdict.h"
> +#include "qobject/qjson.h"
> diff --git a/rust/util/src/qobject/mod.rs b/rust/util/src/qobject/mod.rs
> index e896aba5f3a..292a3c9c238 100644
> --- a/rust/util/src/qobject/mod.rs
> +++ b/rust/util/src/qobject/mod.rs
> @@ -23,6 +23,7 @@
> use common::assert_field_type;
> pub use deserializer::from_qobject;
> pub use error::{Error, Result};
> +use foreign::prelude::*;
> pub use serializer::to_qobject;
>
> use crate::bindings;
> @@ -111,6 +112,22 @@ fn refcnt(&self) -> &AtomicUsize {
> let qobj = self.0.get();
> unsafe { AtomicUsize::from_ptr(addr_of_mut!((*qobj).base.refcnt)) }
> }
> +
> + pub fn to_json(&self) -> String {
> + let qobj = self.0.get();
> + unsafe {
> + let json = bindings::qobject_to_json(qobj);
> + glib_sys::g_string_free(json, glib_sys::GFALSE).into_native()
> + }
> + }
> +
> + pub fn from_json(json: &str) -> std::result::Result<Self, crate::Error> {
> + let c_json = std::ffi::CString::new(json)?;
> + unsafe {
> + crate::Error::with_errp(|errp| bindings::qobject_from_json(c_json.as_ptr(), errp))
> + .map(|qobj| QObject::from_raw(qobj))
> + }
> + }
> }
>
> impl From<()> for QObject {
On 12/5/25 11:04, Markus Armbruster wrote:
> Paolo Bonzini <pbonzini@redhat.com> writes:
>
>> These are used by tests. However it could even be an idea to use
>> serde_json + transcoding and get rid of the C version...
>
> Tell me more!
QEMU's JSON parser produces a QObject. To obtain the same effect, we
can take JSON-string-to-serde deserialization (implemented by
serde_json) and attach it to serde-to-QObject serialization (the thing
in patch 5). That results in a JSON-string-to-QObject function.
Doing it in the other direction (QObject deserializer + JSON-string
serializer) produces a QObject-to-JSON-string function.
For a little more information see https://serde.rs/transcode.html.
Note however that there is no support for push parsing, therefore this
would not replace the balanced-parentheses machinery in
qobject/json-streamer.c, and therefore QMP would still need a minimal lexer.
Grr... I just remembered about interpolation :/ so no, we still need a
parser for libqmp.c.
Paolo
>> Co-authored-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
>> ---
>> rust/util/wrapper.h | 1 +
>> rust/util/src/qobject/mod.rs | 17 +++++++++++++++++
>> 2 files changed, 18 insertions(+)
>>
>> diff --git a/rust/util/wrapper.h b/rust/util/wrapper.h
>> index 0907dd59142..c88820a5e5b 100644
>> --- a/rust/util/wrapper.h
>> +++ b/rust/util/wrapper.h
>> @@ -37,3 +37,4 @@ typedef enum memory_order {
>> #include "qobject/qobject.h"
>> #include "qobject/qlist.h"
>> #include "qobject/qdict.h"
>> +#include "qobject/qjson.h"
>> diff --git a/rust/util/src/qobject/mod.rs b/rust/util/src/qobject/mod.rs
>> index e896aba5f3a..292a3c9c238 100644
>> --- a/rust/util/src/qobject/mod.rs
>> +++ b/rust/util/src/qobject/mod.rs
>> @@ -23,6 +23,7 @@
>> use common::assert_field_type;
>> pub use deserializer::from_qobject;
>> pub use error::{Error, Result};
>> +use foreign::prelude::*;
>> pub use serializer::to_qobject;
>>
>> use crate::bindings;
>> @@ -111,6 +112,22 @@ fn refcnt(&self) -> &AtomicUsize {
>> let qobj = self.0.get();
>> unsafe { AtomicUsize::from_ptr(addr_of_mut!((*qobj).base.refcnt)) }
>> }
>> +
>> + pub fn to_json(&self) -> String {
>> + let qobj = self.0.get();
>> + unsafe {
>> + let json = bindings::qobject_to_json(qobj);
>> + glib_sys::g_string_free(json, glib_sys::GFALSE).into_native()
>> + }
>> + }
>> +
>> + pub fn from_json(json: &str) -> std::result::Result<Self, crate::Error> {
>> + let c_json = std::ffi::CString::new(json)?;
>> + unsafe {
>> + crate::Error::with_errp(|errp| bindings::qobject_from_json(c_json.as_ptr(), errp))
>> + .map(|qobj| QObject::from_raw(qobj))
>> + }
>> + }
>> }
>>
>> impl From<()> for QObject {
>
>
>
Paolo Bonzini <pbonzini@redhat.com> writes: > On 12/5/25 11:04, Markus Armbruster wrote: >> Paolo Bonzini <pbonzini@redhat.com> writes: >> >>> These are used by tests. However it could even be an idea to use >>> serde_json + transcoding and get rid of the C version... >> >> Tell me more! > > QEMU's JSON parser produces a QObject. To obtain the same effect, we > can take JSON-string-to-serde deserialization (implemented by > serde_json) and attach it to serde-to-QObject serialization (the thing > in patch 5). That results in a JSON-string-to-QObject function. > > Doing it in the other direction (QObject deserializer + JSON-string > serializer) produces a QObject-to-JSON-string function. Yes. > For a little more information see https://serde.rs/transcode.html. > > Note however that there is no support for push parsing, therefore this > would not replace the balanced-parentheses machinery in > qobject/json-streamer.c, and therefore QMP would still need a minimal lexer. That push parser... I never liked it. First, it's half-assed: it's a push lexer wed to a pull parser with parenthesis counting. Second, why complicated & half-assed when you can do simple & quarter-assed instead? We could've required "exactly one complete JSON value per line", or some expression separator such as an empty line. > Grr... I just remembered about interpolation :/ so no, we still need a > parser for libqmp.c. Right. Interpolation lets us build QObjects from literal templates with variable scalars or QObjects interpolated. More concise and much easier to read than the equivalend nest of constructor calls. Drawback: chains us to our own, bespoke JSON parser. Out of curiosity: how would we do better than "nest of constructor calls" in Rust?
Il ven 5 dic 2025, 13:16 Markus Armbruster <armbru@redhat.com> ha scritto:
> > Note however that there is no support for push parsing, therefore this
> > would not replace the balanced-parentheses machinery in
> > qobject/json-streamer.c, and therefore QMP would still need a minimal
> lexer.
>
> That push parser... I never liked it. First, it's half-assed: it's a
> push lexer wed to a pull parser with parenthesis counting. Second, why
> complicated & half-assed when you can do simple & quarter-assed instead?
> We could've required "exactly one complete JSON value per line", or some
> expression separator such as an empty line.
>
Hmm not sure I agree, actually I think I disagree. It seems simpler but it
is also different.
Push parsing is not rocket science. It would be easy to write a proper one,
it's just that there is no real reason other than cleanliness.
> Grr... I just remembered about interpolation :/ so no, we still need a
> > parser for libqmp.c.
>
> Right.
>
> Interpolation lets us build QObjects from literal templates with
> variable scalars or QObjects interpolated. More concise and much easier
> to read than the equivalend nest of constructor calls. Drawback: chains
> us to our own, bespoke JSON parser.
>
And also, for similar reasons of practicality, single quotes (which IIRC
also became valid QMP, and that's less excusable).
But while it's a pity, we still get a lot from serde, namely making the
automatic generation of visitor code for structs someone else's problem.
Out of curiosity: how would we do better than "nest of constructor
> calls" in Rust?
>
You'd do that with a quoting macro, i.e.
qobject!({"command": cmd})
where the macro compiles to the nest of constructor calls, like
QObject::from_iter([(c"command", to_qobject(cmd))]).
Paolo
>
Paolo Bonzini <pbonzini@redhat.com> writes:
> Il ven 5 dic 2025, 13:16 Markus Armbruster <armbru@redhat.com> ha scritto:
>
>> > Note however that there is no support for push parsing, therefore this
>> > would not replace the balanced-parentheses machinery in
>> > qobject/json-streamer.c, and therefore QMP would still need a minimal
>> > lexer.
>>
>> That push parser... I never liked it. First, it's half-assed: it's a
>> push lexer wed to a pull parser with parenthesis counting. Second, why
>> complicated & half-assed when you can do simple & quarter-assed instead?
>> We could've required "exactly one complete JSON value per line", or some
>> expression separator such as an empty line.
>
> Hmm not sure I agree, actually I think I disagree. It seems simpler but it
> is also different.
Management applications would be just fine with the different interface.
Human users would be better off. As is, a missing right parenthesis is
met with silence. You can then input whatever you want, and get more
silence until you somehow close the last parenthesis. Quite confusing
unless you already know.
> Push parsing is not rocket science. It would be easy to write a proper one,
> it's just that there is no real reason other than cleanliness.
Me not liking what we have, when what we have works, is no reason to
rewrite it.
Even the lousy usability for humans doesn't justify a rewrite.
>> > Grr... I just remembered about interpolation :/ so no, we still need a
>> > parser for libqmp.c.
>>
>> Right.
>>
>> Interpolation lets us build QObjects from literal templates with
>> variable scalars or QObjects interpolated. More concise and much easier
>> to read than the equivalend nest of constructor calls. Drawback: chains
>> us to our own, bespoke JSON parser.
>
> And also, for similar reasons of practicality, single quotes (which IIRC
> also became valid QMP, and that's less excusable).
Single quotes should've been restricted to internal use, just like
interpolation.
> But while it's a pity, we still get a lot from serde, namely making the
> automatic generation of visitor code for structs someone else's problem.
>
>> Out of curiosity: how would we do better than "nest of constructor
>> calls" in Rust?
>
> You'd do that with a quoting macro, i.e.
>
> qobject!({"command": cmd})
>
> where the macro compiles to the nest of constructor calls, like
> QObject::from_iter([(c"command", to_qobject(cmd))]).
Thanks!
On 12/8/25 10:17, Markus Armbruster wrote: > Paolo Bonzini <pbonzini@redhat.com> writes: > >> Il ven 5 dic 2025, 13:16 Markus Armbruster <armbru@redhat.com> ha scritto: >> >>>> Note however that there is no support for push parsing, therefore this >>>> would not replace the balanced-parentheses machinery in >>>> qobject/json-streamer.c, and therefore QMP would still need a minimal >>>> lexer. >>> >>> That push parser... I never liked it. First, it's half-assed: it's a >>> push lexer wed to a pull parser with parenthesis counting. Second, why >>> complicated & half-assed when you can do simple & quarter-assed instead? >>> We could've required "exactly one complete JSON value per line", or some >>> expression separator such as an empty line. >> >> Hmm not sure I agree, actually I think I disagree. It seems simpler but it >> is also different. > > Management applications would be just fine with the different interface. > > Human users would be better off. As is, a missing right parenthesis is > met with silence. You can then input whatever you want, and get more > silence until you somehow close the last parenthesis. Quite confusing > unless you already know. Not being able to add line breaks to a long JSON command (preparing it in an editor and pasting it into either a "-qmp stdio" terminal or socat/nc) would be a dealbreaker for me. JSON says whitespace is irrelevant. "This standard format but..." is a bad idea that requires extraordinary justification. Not that it matters now 15 years down the line. :) Paolo
© 2016 - 2026 Red Hat, Inc.