8000 Add JSON marshaling library by sudison · Pull Request #2255 · c3lang/c3c · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Add JSON marshaling library #2255

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

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open

Conversation

sudison
Copy link
Contributor
@sudison sudison commented Jun 30, 2025

Refer to request: #1395

  • Add json_marshal.c3 with Go-style API (json::marshal, json::marshal_value, json::marshal_array)
  • Support all primitive types: String, int, float, double, bool
  • Support enums (always marshaled as enum names, regardless of associated values)
  • Support nested structs and arrays of any supported type
  • Use temp allocator for memory-safe operation
  • Comprehensive test suite with 15 test cases covering all features
  • Follow C3 coding style with proper and kindof usage

- Add json_marshal.c3 with Go-style API (json::marshal, json::marshal_value, json::marshal_array)
- Support all primitive types: String, int, float, double, bool
- Support enums (always marshaled as enum names, regardless of associated values)
- Support nested structs and arrays of any supported type
- Use temp allocator for memory-safe operation
- Comprehensive test suite with 15 test cases covering all features
- Follow C3 coding style with proper  and kindof usage
@data-man
Copy link
Contributor

I think that serialization and deserialization are more common and frequently used terms.

- Extract common marshaling logic into marshal_value function
- Simplify marshal() to use marshal_value for all struct fields
- Simplify marshal_array() to use marshal_value for all array elements
- Add array/slice support to marshal_value function
- Reduce code duplication by ~60 lines while maintaining all functionality
- All existing tests continue to pass
@sudison
Copy link
Contributor Author
sudison commented Jun 30, 2025

I think that serialization and deserialization are more common and frequently used terms.

serialization and deserialization are too long for a function name, IMHO. I'd prefer shorter name like, marshal/unmarshal, like in golang.

@sudison sudison mentioned this pull request Jul 1, 2025
Copy link
Contributor
@BWindey BWindey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I do not have any power over merging PRs, I went ahead and looked at the code.
The naming issue aside (I also prefer serialize/deserialize), the code seems to be written in a weird way. You claim to follow the C3 coding style, but the use of doc-comments and optionals is just plain different (and in case of doc-comments just wrong) from what the rest of the standard library uses.

If you're serious about getting this working and up to standards, feel free to join the Discord to chat about improving this. (Discord link: https://discord.gg/qN76R87, can also be found on the C3 website.)

Comment on lines 15 to 19
/**
* Marshal a struct with primitive fields and nested structs to JSON
* @param [in] value The struct value to marshal
* @return The JSON string representation (using temp allocator)
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doc-comments are written with <* *> in C3.

<*
  Marshal a struct with primitive fields and nested structs to JSON
  @param [in] value The struct value to marshal
  @return The JSON string representation (using temp allocator)
*>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment on lines 24 to 27
// Only handle structs
$if $Type.kindof != STRUCT:
return UNSUPPORTED_TYPE?;
$endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the kind of thing you put as an @require in the doc-contract.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

return UNSUPPORTED_TYPE?;
$endif

DString result = dstring::temp();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using temp allocator not in a @pool and then return a string-view to it?

While this might work because it's a macro, it feels wrong, especially because the macro-name does not signify that any allocations would happen that can have an influence outside this function.
Take a look at how String.escape is handled in core/string_escape.c3.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed, adding an allocator as input

Comment on lines 49 to 50
String? field_result = marshal_value($member.get(value));
if (catch err = field_result) return err?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd write this as:

String field_result = marshal_value($member.get(value))!;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment on lines 117 to 118
String? result = json::marshal_value(42);
assert(result!! == "42");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be better to write

String result = json::marshal_value(42)!!;
assert(result == "42");

Idem for all other tests btw.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

assert(json.contains(`"complex_state":"BUSY"`)); // Always uses enum name
assert(json.contains(`"pid":1234`));

// Should be valid JSON format
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Testing if it's actually valid JSON is not 100% covered here, it might be worth writing a small checking function that will check if all keys are in quotes, that key-value pairs are seperated by :, that there are commas where needed...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed, now using json::tparse to parse the json, and verify the result.

@sudison
Copy link
Contributor Author
sudison commented Jul 2, 2025

While I do not have any power over merging PRs, I went ahead and looked at the code. The naming issue aside (I also prefer serialize/deserialize), the code seems to be written in a weird way. You claim to follow the C3 coding style, but the use of doc-comments and optionals is just plain different (and in case of doc-comments just wrong) from what the rest of the standard library uses.

If you're serious about getting this working and up to standards, feel free to join the Discord to chat about improving this. (Discord link: https://discord.gg/qN76R87, can also be found on the C3 website.)

Sorry about the that, most of the code and docs are written by AI, both AI and myself are still learning how to writing c3 code properly. Thanks for your feedback, all are addressed. I will join Discord soon.

Copy link
Contributor
@BWindey BWindey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know how I feel knowing this is done using AI. I feel like it's weird to jump into an open-source project and start letting AI make contributions. To me I'd feel like I want to do contrib 6D47 utions myself to learn the language and become fluid in it.

On the other hand the implementation seems ok.

But this is the last you'll hear from me about this I think, the rest is for @lerno to decide.

result.append(field_result);
};
$endif
$endforeach

result.append_char('}');
return result.str_view();
return result.copy_str(allocator);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not deallocating the DString will leak memory, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed, finally figured out how to use pool and tmem

Comment on lines 228 to 229
Object* parsed = json::tparse_string(json)!!;
defer parsed.free();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're using the temp allocator, no need to free that explicitly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Comment on lines 18 to 19
@param allocator : "The allocator to use for the result"
@param value: "The struct value to marshal"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inconsistent spaces around the :, it seems to be inconsistent in almost every doc-string.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

@sudison
Copy link
Contributor Author
sudison commented Jul 3, 2025

I don't know how I feel knowing this is done using AI. I feel like it's weird to jump into an open-source project and start letting AI make contributions. To me I'd feel like I want to do contributions myself to learn the language and become fluid in it.

On the other hand the implementation seems ok.

But this is the last you'll hear from me about this I think, the rest is for @lerno to decide.

While I use AI to generate code and documentation, I am still a significant contributor to the final code. It takes considerable effort—often an hour or more—to guide the AI to write correct code. Because there is very little C3 code available for training, the AI's knowledge of the language is limited, which requires a lot of manual guidance to get it working correctly.
So, why bother using AI at all?
I believe AI-assisted programming is the future, whether we like it or not.
I wanted to test AI's capabilities on a niche programming language. I've been very satisfied, as it can write decent C3 code.

@lerno
Copy link
Collaborator
lerno commented Jul 3, 2025

I don't have a set opinion on AI. However, any non-trivial feature should currently be incubated before inclusion. So the process is this:

  1. Create a repo for it.
  2. Send a request for feedback (file an issue)
  3. Handle incoming pull request for improvements and fixes.
  4. The library is then either included or instead recommended to be set up as a separate library. There has to be some curation as to what goes into the stdlib as it adds maintenance costs.

@lerno
Copy link
Collaborator
lerno commented Jul 11, 2025

Are you alright with closing this @sudison and offering it through the regular process of an incubator?

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

Successfully merging this pull request may close these issues.

4 participants
0