You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
An S3 generic to perform object transformations prior to json serialise will give package authors full control of their object json serialisation. This is the same concept as the JavaScript toJSON() method on objects.
Some use cases:
Adding / removing elements from lists
Convert element names to camelCase
Formatting dates, rounding numbers, etc
Applying AsIs to elements that must be scalars
A naïve example implementation:
yyjson_mut_val*any_serialise_function(SEXPobject) {
// should be globalSEXPto_json=PROTECT(
Rf_findFun(
Rf_install("to_json"),
Rf_findVarInFrame(R_NamespaceRegistry, Rf_install("yyjsonr"))
)
);
SEXPtrans_object=PROTECT(
Rf_eval(Rf_lang2(to_json, object), R_GlobalEnv)
);
// serialise trans_objectUNPROTECT(2); // 1 if to_json is global// return the yyjson_mut_val*
}
#' @exportto_json<-function(object, ...) UseMethod("to_json")
# A real use-case would be to add AsIs to scalars, drop items that aren't required, camelCase property names etc.# but complete object replacement is possible#' @exportto_json.foobar<-function(object, ...) list(foo="bar")
Executing an R method for each object in the tree has some overhead. In prototyping, I observed around 1.5secs of overhead per 1 000 000 to_json() dispatches. If we assume that most objects won't have a to_json() method, we can avoid much of this overhead by skipping the to_json.default() call.
One approach is to cache the classes implementing to_json() and only dispatch if our input object inherits any of these classes.
E.g.
for (/* to_json classes */) {
if (!Rf_inherits(object, classes[i]) continue;
// invoke to_json()break;
}
In prototyping, with only one s3 method to check, I found this reduced the overhead of serialising types without a to_json() method to approx 20ms per 1 000 000 objects.
I disagree that it's complex, but I made it sound complex by suggesting a custom s3 dispatch to avoid overhead.
In it's simplest form, we'd be calling an S3 method on each object (e.g. a list, dataframe, vector) prior to serialising it. This would be fast for most use-cases, but slower with large list-of-lists.
An eager, recursive transform of objects is definitely doable, but it's not as general as a hook from within yyjsonr itself. The hook allows for defining how types are serialised, which is useful for package authors controlling how their objects are serialised.
An S3 generic to perform object transformations prior to json serialise will give package authors full control of their object json serialisation. This is the same concept as the JavaScript
toJSON()
method on objects.Some use cases:
A naïve example implementation:
Created on 2023-10-09 with reprex v2.0.2
Overhead? Yes there is
Executing an R method for each object in the tree has some overhead. In prototyping, I observed around 1.5secs of overhead per 1 000 000
to_json()
dispatches. If we assume that most objects won't have ato_json()
method, we can avoid much of this overhead by skipping theto_json.default()
call.One approach is to cache the classes implementing
to_json()
and only dispatch if our input object inherits any of these classes.E.g.
In prototyping, with only one s3 method to check, I found this reduced the overhead of serialising types without a
to_json()
method to approx 20ms per 1 000 000 objects.Created on 2023-10-09 with reprex v2.0.2
With
to_json()
Created on 2023-10-09 with reprex v2.0.2
The text was updated successfully, but these errors were encountered: