GH-50724: [C++] Add JsonWriter::WriteValue for simdjson values - #50725
GH-50724: [C++] Add JsonWriter::WriteValue for simdjson values#50725Reranko05 wants to merge 6 commits into
JsonWriter::WriteValue for simdjson values#50725Conversation
|
Could you rebase on main? |
c9a1540 to
bf9e22e
Compare
|
Rebased on main. |
bf9e22e to
eb33737
Compare
eb33737 to
ad6f313
Compare
|
Can you explain in which concrete situation(s) it would be useful? |
|
@pitrou Some of the remaining code needs to convert a ::arrow::Result<std::string> GeospatialGeoArrowCrsToParquetCrs(
const ::arrow::rapidjson::Document& document) {
namespace rj = ::arrow::rapidjson;
if (!document.HasMember("crs") || document["crs"].IsNull()) {
// Parquet GEOMETRY/GEOGRAPHY do not have a concept of a null/missing
// CRS, but an omitted one is more likely to have meant "lon/lat" than
// a truly unspecified one (i.e., Engineering CRS with arbitrary XY units)
return "";
}
const auto& json_crs = document["crs"];
if (json_crs.IsString() && (json_crs == "EPSG:4326" || json_crs == "OGC:CRS84")) {
// crs can be left empty because these cases both correspond to
// longitude/latitude in WGS84 according to the Parquet specification
return "";
} else if (json_crs.IsObject()) {
// Attempt to detect common PROJJSON representations of longitude/latitude and return
// an empty crs to maximize compatibility with readers that do not implement CRS
// support. PROJJSON stores this in the "id" member like:
// {..., "id": {"authority": "...", "code": "..."}}
if (json_crs.HasMember("id")) {
const auto& identifier = json_crs["id"];
if (identifier.HasMember("authority") && identifier.HasMember("code")) {
if (identifier["authority"] == "OGC" && identifier["code"] == "CRS84") {
return "";
} else if (identifier["authority"] == "EPSG" && identifier["code"] == "4326") {
return "";
} else if (identifier["authority"] == "EPSG" && identifier["code"].IsInt() &&
identifier["code"].GetInt() == 4326) {
return "";
}
}
}
}
// If we could not detect a longitude/latitude CRS, just write the string to the
// LogicalType crs (being sure to unescape a JSON string into a regular string)
if (json_crs.IsString()) {
return json_crs.GetString();
} else {
rj::StringBuffer buffer;
rj::Writer<rj::StringBuffer> writer(buffer);
json_crs.Accept(writer);
return buffer.GetString();
}
} |
da5440b to
042951d
Compare
| return Status::OK(); | ||
| }, | ||
|
|
||
| [&](sj::value number_value) -> Status { |
There was a problem hiding this comment.
Shouldn't this be a sj::number instead? Then we should be left with no results within the lambda body?
There was a problem hiding this comment.
I kept sj::value because we still need get_number_type() to detect big integers, and simdjson::to_json_string() also requires the original value. Passing sj::number would move part of the number handling into VisitJsonValue. Does that seem like a reasonable tradeoff to you?
There was a problem hiding this comment.
Ah, that is a rather annoying problem, I see. As this will be the only user of the library-function, we cannot see how good a solution will fit more general usages.
I see two options:
- Provide another
BigIntegerFnto the visitor - Move the switch on
number_typealso to the visitor and provide all four call-backs {DoubleFn,Int64Fn,Uint64Fn,BigIntegerFn}, dropping the argumentNumberFn, instead. This would correspond to thenumber_typeenum insimdjson.
enum class number_type {
floating_point_number=1, /// a binary64 number
signed_integer, /// a signed integer that fits in a 64-bit word using two's complement
unsigned_integer, /// a positive integer larger or equal to 1<<63
big_integer /// a big integer that does not fit in a 64-bit word
};
If all options of number_type could fit into the number-struct's representation this would not be a problem, but I guess this has been done because implementations that want to error on big_integer would need to pay a performance cost otherwise.
Rationale for this change
This PR continues the simdjson migration by adding support for serializing
simdjson::ondemand::valuedirectly withJsonWriter.This provides a reusable API for future migration work and avoids requiring callers to implement their own recursive serialization logic. It also introduces a shared helper for dispatching
simdjson::ondemand::valuebased on its JSON type, reducing duplicated type dispatch and extraction logic.What changes are included in this PR?
Add
JsonWriter::WriteValue(simdjson::ondemand::value).Add
VisitJsonValueto centralize JSON type dispatch andsimdjsonvalue extraction.Recursively serialize:
Add unit tests covering:
Use
simdjson::ondemand::document::get_value()in tests to obtain the rootondemand::valuebefore serialization.Are these changes tested?
Yes.
Added unit tests for
JsonWriter::WriteValuecovering the supported JSON value types and nested structures.Are there any user-facing changes?
No.
Closes: #50724
JsonWriter::WriteValuefor simdjson values #50724