Skip to content

feat(utils): add NumpyJSONDecoder for deserializing encoded types - #8623

Open
Hardik K (hardikkaurani) wants to merge 2 commits into
microsoft:mainfrom
hardikkaurani:feat/numpy-json-decoder
Open

Hardik K (hardikkaurani) wants to merge 2 commits into
microsoft:mainfrom
hardikkaurani:feat/numpy-json-decoder

Conversation

@hardikkaurani

Copy link
Copy Markdown

Description

Fixes #2931.

QCoDeS provides NumpyJSONEncoder to support serialization of types that Python's standard json module cannot handle out of the box, such as complex numbers (encoded as {"__dtype__": "complex", "re": ..., "im": ...}) and uncertainties.UFloat objects (encoded as {"__dtype__": "UFloat", "nominal_value": ..., "std_dev": ...}).

However, there was no corresponding decoder class to reconstruct these objects when reading JSON data back. Users decoding serialized dictionaries were left with raw dictionaries containing __dtype__ keys instead of restored Python objects.

This PR introduces NumpyJSONDecoder as a subclass of json.JSONDecoder with a default object_hook that recognizes these encoded structures and restores them into their corresponding Python types:

  • Objects with __dtype__ == "complex" and "re", "im" fields are converted back to Python complex.
  • Objects with __dtype__ == "UFloat" and "nominal_value", "std_dev" fields are converted back to uncertainties.ufloat.
  • Custom object_hook arguments can still be passed to customize or chain decoding logic.
  • Standard JSON values, strings, lists, numbers, and plain dictionaries decode untouched.

Testing

Added comprehensive unit tests to tests/helpers/test_json_encoder.py:

  • test_numpy_json_decoder_complex: Verifies decoding and type restoration for complex numbers.
  • test_numpy_json_decoder_ufloat: Verifies roundtrip restoration of uncertainties.ufloat.
  • test_numpy_json_decoder_roundtrip_examplemetadata: Verifies decoding of complex numbers and plain types in the standard EXAMPLEMETADATA structure.
  • test_numpy_json_decoder_custom_object_hook: Verifies user-provided object_hook customization.

All 17 tests in tests/helpers/test_json_encoder.py pass cleanly. ruff check and ruff format are clean across all modified files.

@hardikkaurani
Hardik K (hardikkaurani) requested a review from a team as a code owner October 17, 2026 08:44
@hardikkaurani

Copy link
Copy Markdown
Author

Hello Mikhail Astafev (@astafan8),

I have submitted this pull request to resolve issue #2931 by providing a dedicated NumpyJSONDecoder class to complement NumpyJSONEncoder.

The decoder inspects encoded dictionary payloads and restores complex and uncertainties.UFloat instances from their serialized representations while allowing custom object_hook chaining and leaving standard JSON primitives untouched.

Unit tests covering typed roundtrips, custom hooks, and existing metadata examples have been added to tests/helpers/test_json_encoder.py, with all tests and formatting checks passing cleanly. Please review when convenient. Thank you for your time.

@codecov

codecov Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 72.59%. Comparing base (3d24a6a) to head (9ace8cb).
⚠️ Report is 4 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8623      +/-   ##
==========================================
+ Coverage   72.58%   72.59%   +0.01%     
==========================================
  Files         307      307              
  Lines       32409    32422      +13     
==========================================
+ Hits        23525    23538      +13     
  Misses       8884     8884              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@hardikkaurani

Copy link
Copy Markdown
Author

Hi Jens Hedegaard Nielsen (@jenshnielsen),

Could you please take a look at this PR when you have a moment? It adds NumpyJSONDecoder to complement NumpyJSONEncoder (resolving #2931) for restoring complex and uncertainties.UFloat serialized objects.

All CI matrix jobs (pytest, mypy, docs, pre-commit, codecov) have passed across Python 3.12–3.15 on Linux and Windows. Thank you for your time!

@jenshnielsen

Copy link
Copy Markdown
Collaborator

Hardik K (@hardikkaurani) Thanks for the contribution. The code looks fine. The reason we did not originally add a decoder is that there is no way to plug it into the json decoder directly. I assume your intention with this is that the user should call it directly? It would be nice if this worked automatically but I guess a manual decoder is better than nothing but in that case I think we should add an example documenting it

…wsfragment

Document how to use NumpyJSONDecoder with json.loads, json.load, and the default_object_hook. Also add regression tests for stream loading and object hook usage, and add a towncrier newsfragment for PR 8623.

Signed-off-by: Hardik Kaurani <hardikkaurani1@gmail.com>
@hardikkaurani

Hardik K (hardikkaurani) commented Oct 30, 2026 •

Copy link
Copy Markdown
Author

Hi Jens Hedegaard Nielsen (@jenshnielsen)

Thank you for the review and helpful feedback!

You're absolutely right that Python's standard-library json module does not provide a global pluggable decoder registry like JSONEncoder.default(). Therefore, NumpyJSONDecoder or its hook must be explicitly provided during deserialization.

I've addressed this in commit [d3279ab89e2e456fc1c934447d43879a82d4384f](https://github.com/scikit-hep/boost-histogram/commit/d3279ab89e2e456fc1c934447d43879a82d4384f):

  1. Documentation & Examples: Expanded NumpyJSONDecoder documentation with examples for json.loads(..., cls=NumpyJSONDecoder), direct object_hook usage, and file-stream deserialization using json.load(). Added Google-style documentation to default_object_hook and cross-referenced the decoder in NumpyJSONEncoder.

  2. Changelog: Added docs/changes/newsfragments/8623.new following the project's Towncrier conventions.

  3. Tests & Validation: Added regression tests covering direct hook usage, stream loading, and fallback behavior for unrecognized dictionaries.

All 20 targeted pytest tests pass. Ruff checks, formatting, mypy, and Towncrier validation also pass cleanly.

Thank you again for the guidance. Please let me know if any further adjustments are needed!

This branch has not been deployed

No deployments
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.

NumpyJSONDecoder

2 participants