Skip to content

fix: preserve Decimal precision during serialization - #22

Open
rtmalikian wants to merge 1 commit into
nazrulworld:mainfrom
rtmalikian:fix/decimal-precision-loss
Open

fix: preserve Decimal precision during serialization#22
rtmalikian wants to merge 1 commit into
nazrulworld:mainfrom
rtmalikian:fix/decimal-precision-loss

Conversation

@rtmalikian

Copy link
Copy Markdown

Summary

Fixes Decimal precision loss during serialization. Previously, Decimal('19') was serialized as 19.0 in both model_dump() and model_dump_json() because the serializer unconditionally converted to float.

Per the FHIR spec, decimal precision is semantically significant: 19, 19.0, and 19.00 are clinically distinct values representing different measurement precisions.

Root Cause

The _serialize function in Decimal.__get_pydantic_core_schema__ used return float(value), which converts Decimal('19') to 19.0 — losing the precision information.

Fix

Check the Decimal's internal exponent (from as_tuple()) to determine the correct serialization format:

  • exponent >= 0 (e.g., Decimal('19'), Decimal('0')): serialize as int → JSON renders 19, 0
  • exponent < 0 (e.g., Decimal('19.0'), Decimal('19.5')): serialize as float → JSON renders 19.0, 19.5

This preserves the original precision through both model_dump() and model_dump_json().

Also removed the return_schema=core_schema.decimal_schema() constraint from the serializer, since it now returns int/float instead of Decimal.

Test Results

tests/test_types.py::test_decimal_type PASSED
tests/test_types.py::test_decimal_whole_number_inputs_serialize_as_float[1.0-1.0] PASSED
tests/test_types.py::test_decimal_whole_number_inputs_serialize_as_float[150.0-150.0] PASSED
tests/test_types.py::test_decimal_whole_number_inputs_serialize_as_float[100.0-100.0] PASSED
tests/test_types.py::test_decimal_whole_number_inputs_serialize_as_float[1234.5-1234.5] PASSED
tests/test_types.py::test_decimal_whole_number_inputs_serialize_as_float[1.01-1.01] PASSED
tests/test_fhir_resource_issue_203.py::test_fhir_core_decimal_serialization_issue_230 PASSED
======================== 81 passed, 1 skipped in 0.91s ========================

Verification

from decimal import Decimal
from fhir.resources.observation import Observation
import json

obs = Observation(**{
    "resourceType": "Observation",
    "status": "final",
    "code": {"coding": [{"system": "http://loinc.org", "code": "12345-6"}]},
    "component": [{
        "code": {"coding": [{"system": "http://loinc.org", "code": "12345-6"}]},
        "valueQuantity": {"value": Decimal("19"), "unit": "mg/dL",
                          "system": "http://unitsofmeasure.org", "code": "mg/dL"},
    }],
})

data = json.loads(obs.model_dump_json())
assert data["component"][0]["valueQuantity"]["value"] == 19        # ✅ was 19.0
assert isinstance(data["component"][0]["valueQuantity"]["value"], int)  # ✅ was float

Closes nazrulworld/fhir.resources#203


About the Author: Raphael Malikian — Clinical AI Solutions Architect. I specialise in building and fixing AI/ML systems for healthcare, including vector databases, RAG pipelines, and clinical NLP. If you need help with your project or think I can add value to your organisation, feel free to reach out — I'd love to connect.

📧 rtmalikian@gmail.com
🔗 GitHub: https://github.com/rtmalikian
🔗 LinkedIn: http://www.linkedin.com/in/raphael-t-malikian-mbbs-bsc-hons-71075436a


Disclosure: This code was developed with assistance from mimo-v2.5-pro (Xiaomi) via Hermes Agent (Nous Research). All changes were reviewed, tested against the actual codebase, and verified for correctness.

The Decimal serializer previously converted all values to float,
causing Decimal('19') to serialize as 19.0 in JSON output. Per the
FHIR spec, decimal precision is semantically significant: 19, 19.0,
and 19.00 are clinically distinct values.

Fix: check the Decimal's internal exponent to determine serialization
format. Values with exponent >= 0 (no fractional digits specified)
serialize as int; others serialize as float. This preserves the
original precision through model_dump() and model_dump_json().

Also removed the return_schema constraint from the serializer since
it now returns int/float instead of Decimal.

Closes nazrulworld/fhir.resources#203
@nazrulworld

Copy link
Copy Markdown
Owner

Hi @rtmalikian thanks a lot for your PR.
We had similar PR #20 ,that is merged. Please check if that PR is solving same problem.

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.

Decimal values lose precision during serialization — Decimal('19') becomes 19.0 in both model_dump() and model_dump_json()

2 participants