22# SPDX-License-Identifier: Apache-2.0
33
44import logging
5+ import re
56from copy import deepcopy
7+ from decimal import ROUND_HALF_UP , Decimal
68from typing import Any , overload
79
810from jsonschema import Draft202012Validator , ValidationError , validators
@@ -70,17 +72,30 @@ def extend_jsonschema_validator_with_pruning(validator):
7072 return validators .extend (validator , {"additionalProperties" : prune_additional_properties })
7173
7274
73- def _has_number_string_anyof (schema : dict ) -> bool :
74- """Check if schema has anyOf with both number and string (Pydantic Decimal pattern)."""
75+ def _get_decimal_info_from_anyof (schema : dict ) -> tuple [bool , int | None ]:
76+ """Check if schema is a Decimal anyOf and extract decimal places.
77+
78+ Returns (is_decimal, decimal_places) where decimal_places is None if no constraint.
79+ """
7580 any_of = schema .get ("anyOf" )
7681 if not isinstance (any_of , list ):
77- return False
78- types = {item .get ("type" ) for item in any_of }
79- return "number" in types and "string" in types
82+ return False , None
83+
84+ has_number = any (item .get ("type" ) == "number" for item in any_of )
85+ if not has_number :
86+ return False , None
87+
88+ for item in any_of :
89+ if item .get ("type" ) == "string" and "pattern" in item :
90+ match = re .search (r"\\d\{0,(\d+)\}" , item ["pattern" ])
91+ if match :
92+ return True , int (match .group (1 ))
93+ return True , None # Decimal without precision constraint
94+ return False , None
8095
8196
8297def normalize_decimal_fields (obj : DataObjectT , schema : JSONSchemaT ) -> DataObjectT :
83- """Convert numeric values to strings for Decimal-like anyOf fields."""
98+ """Normalize Decimal-like anyOf fields to floats with proper precision ."""
8499 if not isinstance (obj , dict ):
85100 return obj
86101
@@ -97,8 +112,13 @@ def normalize_decimal_fields(obj: DataObjectT, schema: JSONSchemaT) -> DataObjec
97112 obj [key ] = normalize_decimal_fields (value , schema )
98113 elif isinstance (value , list ):
99114 obj [key ] = [normalize_decimal_fields (v , schema ) if isinstance (v , dict ) else v for v in value ]
100- elif isinstance (value , (int , float )) and not isinstance (value , bool ) and _has_number_string_anyof (field_schema ):
101- obj [key ] = str (value )
115+ elif isinstance (value , (int , float , str )) and not isinstance (value , bool ):
116+ is_decimal , decimal_places = _get_decimal_info_from_anyof (field_schema )
117+ if is_decimal :
118+ d = Decimal (str (value ))
119+ if decimal_places is not None :
120+ d = d .quantize (Decimal (f"0.{ '0' * decimal_places } " ), rounding = ROUND_HALF_UP )
121+ obj [key ] = float (d )
102122
103123 return obj
104124
0 commit comments