nazrulworld / fhir.resources

FHIR Resources https://www.hl7.org/fhir/resourcelist.html
https://pypi.org/project/fhir.resources/
Other
365 stars 104 forks source link

YAML datetime is not serialized to the propor iso format #96

Closed Healthedata1 closed 2 years ago

Healthedata1 commented 2 years ago

Description

The datetime is not serialized to the proper iso format when using the yaml() method. This leads to errors when converting to json and with FHIR validation.

What I Did

from pathlib import Path
from fhir.resources import construct_fhir_element
from datetime import datetime, timedelta

delta = timedelta(hours = 12)

pd = construct_fhir_element('Period', {})

pd.start = datetime.now() - delta
pd.end = datetime.now()

pd
Period(resource_type='Period', fhir_comments=None, extension=None, id=None, id__ext=None, end=datetime.datetime(2022, 3, 29, 11, 48, 37, 482248), end__ext=None, start=datetime.datetime(2022, 3, 28, 23, 48, 37, 481725), start__ext=None)
print(f'JSON serialization = {pd.json(indent=True)}')
print(f'YAML serialization = \n{pd.yaml(indent=True)}')
JSON serialization = {
  "start": "2022-03-28T23:48:37.481725",
  "end": "2022-03-29T11:48:37.482248"
}
YAML serialization = 
start: 2022-03-28 23:48:37.481725
end: 2022-03-29 11:48:37.482248
nazrulworld commented 2 years ago

https://yaml.org/type/timestamp.html although without T (space only) is a valid format according to YAML specification. But I fully agree with you, DateTime should represent according to FHIR specification and fortunately, that will comply with YAML specification as well.

Healthedata1 commented 2 years ago

Yes FHIR specifies the iso8601 format with a capital 'T' and I unexpectedly get a yaml to json error when converting using the standard modules.

import sys, yaml, json; json.dump(yaml.full_load(sys.stdin), sys.stdout, indent=4)' < $yaml_file > $json_file

but I think that is because it is not encased in quotes.

Eric

Eric M Haas, DVM, MS Health eData Inc 35 Crescent Avenue, Sausalito, CA 94965 707.227.2608|Skype: haas.eric1 @.***

On Wed, Mar 30, 2022 at 12:58 AM Md Nazrul Islam @.***> wrote:

https://yaml.org/type/timestamp.html although without T (space only) is a valid format according to YAML specification. But I fully agree with you, DateTime should represent according to FHIR specification and fortunately, that will comply with YAML specification as well.

— Reply to this email directly, view it on GitHub https://github.com/nazrulworld/fhir.resources/issues/96#issuecomment-1082751036, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABYRJ2WVC4KHPDPLTYPAXKLVCQCRJANCNFSM5R7LRVOA . You are receiving this because you authored the thread.Message ID: @.***>

nazrulworld commented 2 years ago

@Healthedata1 check out the new release https://pypi.org/project/fhir.resources/6.2.2/

Healthedata1 commented 2 years ago

Thanks, I'll check it out. Eric M Haas, DVM, MS Health eData Inc 35 Crescent Avenue, Sausalito, CA 94965 707.227.2608|Skype: haas.eric1 @.***

On Sat, Apr 2, 2022 at 2:14 AM Md Nazrul Islam @.***> wrote:

@Healthedata1 https://github.com/Healthedata1 check out the new release https://pypi.org/project/fhir.resources/6.2.2/

— Reply to this email directly, view it on GitHub https://github.com/nazrulworld/fhir.resources/issues/96#issuecomment-1086598204, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABYRJ2UYB4G5U5UI7Y6MKB3VDAFYPANCNFSM5R7LRVOA . You are receiving this because you were mentioned.Message ID: @.***>

Healthedata1 commented 2 years ago

this fixes the main issue but one more is that the YAML serialized datetime/time is not wrapped in quotes and caused issues when try to convert yaml to json. So you cannot round trip from fhir.resource model to yaml to json to fhir.resource.

see below:

from pathlib import Path
from fhir.resources import construct_fhir_element
from datetime import datetime, timedelta
import yaml, json

delta = timedelta(hours = 12)

pd = construct_fhir_element('Period', {})

pd.start = datetime.now() - delta
pd.end = datetime.now()

pd
Period(resource_type='Period', fhir_comments=None, extension=None, id=None, id__ext=None, end=datetime.datetime(2022, 4, 8, 8, 16, 37, 223359), end__ext=None, start=datetime.datetime(2022, 4, 7, 20, 16, 37, 223286), start__ext=None)
pd_json=pd.json()
pd_json
'{"start":"2022-04-07T20:16:37.223286","end":"2022-04-08T08:16:37.223359"}'
pd_dict = json.loads(pd_json)
yaml.dump(pd_dict)
"end: '2022-04-08T08:16:37.223359'\nstart: '2022-04-07T20:16:37.223286'\n"
pd_yaml=pd.yaml()
pd_yaml
'start: 2022-04-07T20:16:37.223286\nend: 2022-04-08T08:16:37.223359\n'
pd_dict = yaml.safe_load(pd_yaml)
json.dumps(pd_dict)
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

Input In [22], in <cell line: 2>()
      1 pd_dict = yaml.safe_load(pd_yaml)
----> 2 json.dumps(pd_dict)

File ~/.pyenv/versions/3.10.2/lib/python3.10/json/__init__.py:231, in dumps(obj, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, default, sort_keys, **kw)
    226 # cached encoder
    227 if (not skipkeys and ensure_ascii and
    228     check_circular and allow_nan and
    229     cls is None and indent is None and separators is None and
    230     default is None and not sort_keys and not kw):
--> 231     return _default_encoder.encode(obj)
    232 if cls is None:
    233     cls = JSONEncoder

File ~/.pyenv/versions/3.10.2/lib/python3.10/json/encoder.py:199, in JSONEncoder.encode(self, o)
    195         return encode_basestring(o)
    196 # This doesn't pass the iterator directly to ''.join() because the
    197 # exceptions aren't as detailed.  The list call should be roughly
    198 # equivalent to the PySequence_Fast that ''.join() would do.
--> 199 chunks = self.iterencode(o, _one_shot=True)
    200 if not isinstance(chunks, (list, tuple)):
    201     chunks = list(chunks)

File ~/.pyenv/versions/3.10.2/lib/python3.10/json/encoder.py:257, in JSONEncoder.iterencode(self, o, _one_shot)
    252 else:
    253     _iterencode = _make_iterencode(
    254         markers, self.default, _encoder, self.indent, floatstr,
    255         self.key_separator, self.item_separator, self.sort_keys,
    256         self.skipkeys, _one_shot)
--> 257 return _iterencode(o, 0)

File ~/.pyenv/versions/3.10.2/lib/python3.10/json/encoder.py:179, in JSONEncoder.default(self, o)
    160 def default(self, o):
    161     """Implement this method in a subclass such that it returns
    162     a serializable object for ``o``, or calls the base implementation
    163     (to raise a ``TypeError``).
   (...)
    177 
    178     """
--> 179     raise TypeError(f'Object of type {o.__class__.__name__} '
    180                     f'is not JSON serializable')

TypeError: Object of type datetime is not JSON serializable
Healthedata1 commented 2 years ago

After reviewing the PyYaml documentation I realize there is no issue here and it lies with my usage of the yaml package.