Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Convert serde_json::Value to Ion #803

Closed
jplock opened this issue Aug 10, 2024 · 5 comments
Closed

Convert serde_json::Value to Ion #803

jplock opened this issue Aug 10, 2024 · 5 comments
Labels
question Further information is requested

Comments

@jplock
Copy link

jplock commented Aug 10, 2024

My API allows people to pass arbitrary JSON payloads in a request body. Through Axum I’m converting the payload into a serde_json::Value. I’d like to convert this into an Ion binary value and store it (in DynamoDB).

What’s the best way to do the conversion if I don’t care about the schema?

@jplock jplock added the question Further information is requested label Aug 10, 2024
@popematt
Copy link
Contributor

if I don’t care about the schema?

I'm assuming you mean "without serde-annotated structs".

You would need to define a mapping from serde_json::Value to ion_rs::Element, such as this:

use ion_rs::{Element, IonType, List, Sequence, Struct};
use serde_json::{Value};

fn json_to_ion(value: Value) -> Element {
    match value {
        Value::Null => Element::null(IonType::Null),
        Value::Bool(b) => Element::boolean(b),
        Value::Number(n) => {
            todo!("Choose a number conversion strategy")

            // == Convert Number to Ion float and Ion int ==
            // if let Some(i) = n.as_i64() {
            //     Element::int(i)
            // } else if let Some(i) = n.as_u64() {
            //     Element::int(i)
            // } else if let Some(f) = n.as_f64() {
            //     Element::float(f)
            // } else {
            //     unreachable!()
            // }

            // == Convert all Number to Ion decimal ==
            // if let Some(i) = n.as_i64() {
            //     Element::decimal(i.into())
            // } else if let Some(i) = n.as_u64() {
            //     Element::decimal(i.into())
            // } else if let Some(f) = n.as_f64() {
            //     Element::decimal(f.try_into().expect("Infinite or NaN values are not JSON numbers."))
            // } else {
            //     unreachable!()
            // }

            // One could also convert float Numbers to Ion decimal and integer Numbers to Ion int
        }
        Value::String(s) => Element::string(s),
        Value::Array(values) => List(
            values
                .into_iter()
                .map(|v| json_to_ion(v))
                .collect::<Sequence>(),
        )
        .into(),
        Value::Object(values) => values
            .into_iter()
            .map(|(k, v)| (k, json_to_ion(v)))
            .collect::<Struct>()
            .into(),
    }
}

(Constructing the List is more awkward than it needs to be; I've created #811 to address that.)

Then, assuming you are using ion-rs v1.0.0-rc.6, to write the data to binary Ion, you can use encode_as(Binary)

Does this help? (Have I understood your question correctly?)

@jplock
Copy link
Author

jplock commented Aug 12, 2024

Yes, correct. No serde annotated structs. This gives me a good starting point. Thanks!

@jplock jplock closed this as completed Aug 12, 2024
@jplock
Copy link
Author

jplock commented Aug 12, 2024

Follow up question, is there anyway to go from a Vec representing a JSON byte string directly into Ion or do I need to convert to a serde_json::Value first?

@popematt
Copy link
Contributor

By "Vec representing a JSON byte string" do you just mean serialized JSON?

All JSON is valid text-encoded Ion, so you could skip reading as JSON and read it as if it were Ion using Element::read_one<A: AsRef<[u8]>>(data: A). Then it becomes a simple matter of writing the Element to binary Ion.

The disadvantage to this approach, however, is that you have no specific control over how numbers are converted. JSON has only one "number" type, whereas Ion has three. When up-converting from JSON, the Ion reader will follow the same rules as parsing text-encoded Ion.

  • If the JSON number has an e, it will be interpreted as an Ion float
  • If the JSON number has no e but does have a ., it will be interpreted as an Ion decimal
  • If the JSON number has no e or ., it will be interpreted as an Ion int

@jplock
Copy link
Author

jplock commented Aug 12, 2024

Yes serialized JSON. The read_one method sounds like it will work for my use case. I’ll try it out. Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants