# Path: tincan/base.py
# class Base(object):
#     _props = []
# 
#     def __init__(self, *args, **kwargs):
#         """Initializes an object by checking the provided arguments
#         against lists defined in the individual class. If required
#         properties are defined, this method will set them to None by default.
#         Optional properties will be ignored if they are not provided. The
#         class may provide custom setters for properties, in which case those
#         setters (see __setattr__ below).
# 
#         """
#         if hasattr(self, '_props_req') and self._props_req:
#             list(map(lambda k: setattr(self, k, None), self._props_req))
# 
#         new_kwargs = {}
#         for obj in args:
#             new_kwargs.update(obj if isinstance(obj, dict) else vars(obj))
# 
#         new_kwargs.update(kwargs)
# 
#         for key, value in new_kwargs.items():
#             setattr(self, key, value)
# 
#     def __setattr__(self, attr, value):
#         """Makes sure that only allowed properties are set. This method will
#         call the proper attribute setter as defined in the class to provide
#         additional error checking
# 
#         :param attr: the attribute being set
#         :type attr: str
#         :param value: the value to set
# 
#         """
#         if attr.startswith('_') and attr[1:] in self._props:
#             super(Base, self).__setattr__(attr, value)
#         elif attr not in self._props:
#             raise AttributeError(
#                 f"Property '{attr}' cannot be set on a 'tincan.{self.__class__.__name__}' object."
#                 f"Allowed properties: {', '.join(self._props)}"
#             )
#         else:
#             super(Base, self).__setattr__(attr, value)
# 
#     def __eq__(self, other):
#         return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
# 
# Path: tincan/version.py
# class Version(object):
#     supported = [
#         '1.0.3',
#         '1.0.2',
#         '1.0.1',
#         '1.0.0',
#     ]
#     latest = supported[0]
# 
# Path: tincan/conversions/iso8601.py
# def jsonify_datetime(value):
#     assert isinstance(value, datetime.datetime)
#     return value.isoformat()
# 
# def jsonify_timedelta(value):
#     """Converts a `datetime.timedelta` to an ISO 8601 duration
#     string for JSON-ification.
# 
#     :param value: something to convert
#     :type value: datetime.timedelta
#     :return: the value after conversion
#     :rtype unicode
# 
#     """
# 
#     assert isinstance(value, datetime.timedelta)
# 
#     # split seconds to larger units
#     seconds = value.total_seconds()
#     minutes, seconds = divmod(seconds, 60)
#     hours, minutes = divmod(minutes, 60)
#     days, hours = divmod(hours, 24)
# 
#     days, hours, minutes = list(map(int, (days, hours, minutes)))
#     seconds = round(seconds, 6)
# 
#     # build date
#     date = ''
#     if days:
#         date = '%sD' % days
# 
#     # build time
#     time = 'T'
# 
#     # hours
#     bigger_exists = date or hours
#     if bigger_exists:
#         time += '{:02}H'.format(hours)
# 
#     # minutes
#     bigger_exists = bigger_exists or minutes
#     if bigger_exists:
#         time += '{:02}M'.format(minutes)
# 
#     # seconds
#     if seconds.is_integer():
#         seconds = '{:02}'.format(int(seconds))
#     else:
#         # 9 chars long w/leading 0, 6 digits after decimal
#         seconds = '%09.6f' % seconds
#         # remove trailing zeros
#         seconds = seconds.rstrip('0')
# 
#     time += '{}S'.format(seconds)
# 
#     return 'P' + date + time


import json
import uuid
import datetime
import re
from tincan.base import Base
from tincan.version import Version
from tincan.conversions.iso8601 import jsonify_datetime, jsonify_timedelta
        that can be be propagated from class constructors.

        :param json_data: The JSON string to convert
        :type json_data: str | unicode

        :raises: TypeError, ValueError, LanguageMapInitError
        """

        data = json.loads(json_data)
        result = cls(data)
        if hasattr(result, "_from_json"):
            result._from_json()
        return result

    def to_json(self, version=Version.latest):
        """Tries to convert an object into a JSON representation and return
        the resulting string

        An Object can define how it is serialized by overriding the as_version()
        implementation. A caller may further define how the object is serialized
        by passing in a custom encoder. The default encoder will ignore
        properties of an object that are None at the time of serialization.

        :param version: The version to which the object must be serialized to.
        This will default to the latest version supported by the library.
        :type version: str | unicode

        """
        return json.dumps(self.as_version(version))


# Path: tincan/base.py
# class Base(object):
#     _props = []
# 
#     def __init__(self, *args, **kwargs):
#         """Initializes an object by checking the provided arguments
#         against lists defined in the individual class. If required
#         properties are defined, this method will set them to None by default.
#         Optional properties will be ignored if they are not provided. The
#         class may provide custom setters for properties, in which case those
#         setters (see __setattr__ below).
# 
#         """
#         if hasattr(self, '_props_req') and self._props_req:
#             list(map(lambda k: setattr(self, k, None), self._props_req))
# 
#         new_kwargs = {}
#         for obj in args:
#             new_kwargs.update(obj if isinstance(obj, dict) else vars(obj))
# 
#         new_kwargs.update(kwargs)
# 
#         for key, value in new_kwargs.items():
#             setattr(self, key, value)
# 
#     def __setattr__(self, attr, value):
#         """Makes sure that only allowed properties are set. This method will
#         call the proper attribute setter as defined in the class to provide
#         additional error checking
# 
#         :param attr: the attribute being set
#         :type attr: str
#         :param value: the value to set
# 
#         """
#         if attr.startswith('_') and attr[1:] in self._props:
#             super(Base, self).__setattr__(attr, value)
#         elif attr not in self._props:
#             raise AttributeError(
#                 f"Property '{attr}' cannot be set on a 'tincan.{self.__class__.__name__}' object."
#                 f"Allowed properties: {', '.join(self._props)}"
#             )
#         else:
#             super(Base, self).__setattr__(attr, value)
# 
#     def __eq__(self, other):
#         return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
# 
# Path: tincan/version.py
# class Version(object):
#     supported = [
#         '1.0.3',
#         '1.0.2',
#         '1.0.1',
#         '1.0.0',
#     ]
#     latest = supported[0]
# 
# Path: tincan/conversions/iso8601.py
# def jsonify_datetime(value):
#     assert isinstance(value, datetime.datetime)
#     return value.isoformat()
# 
# def jsonify_timedelta(value):
#     """Converts a `datetime.timedelta` to an ISO 8601 duration
#     string for JSON-ification.
# 
#     :param value: something to convert
#     :type value: datetime.timedelta
#     :return: the value after conversion
#     :rtype unicode
# 
#     """
# 
#     assert isinstance(value, datetime.timedelta)
# 
#     # split seconds to larger units
#     seconds = value.total_seconds()
#     minutes, seconds = divmod(seconds, 60)
#     hours, minutes = divmod(minutes, 60)
#     days, hours = divmod(hours, 24)
# 
#     days, hours, minutes = list(map(int, (days, hours, minutes)))
#     seconds = round(seconds, 6)
# 
#     # build date
#     date = ''
#     if days:
#         date = '%sD' % days
# 
#     # build time
#     time = 'T'
# 
#     # hours
#     bigger_exists = date or hours
#     if bigger_exists:
#         time += '{:02}H'.format(hours)
# 
#     # minutes
#     bigger_exists = bigger_exists or minutes
#     if bigger_exists:
#         time += '{:02}M'.format(minutes)
# 
#     # seconds
#     if seconds.is_integer():
#         seconds = '{:02}'.format(int(seconds))
#     else:
#         # 9 chars long w/leading 0, 6 digits after decimal
#         seconds = '%09.6f' % seconds
#         # remove trailing zeros
#         seconds = seconds.rstrip('0')
# 
#     time += '{}S'.format(seconds)
# 
#     return 'P' + date + time


# Path: tincan/serializable_base.py
import json
import uuid
import datetime
import re
from tincan.base import Base
from tincan.version import Version
from tincan.conversions.iso8601 import jsonify_datetime, jsonify_timedelta

        that can be be propagated from class constructors.

        :param json_data: The JSON string to convert
        :type json_data: str | unicode

        :raises: TypeError, ValueError, LanguageMapInitError
        """

        data = json.loads(json_data)
        result = cls(data)
        if hasattr(result, "_from_json"):
            result._from_json()
        return result

    def to_json(self, version=Version.latest):
        """Tries to convert an object into a JSON representation and return
        the resulting string

        An Object can define how it is serialized by overriding the as_version()
        implementation. A caller may further define how the object is serialized
        by passing in a custom encoder. The default encoder will ignore
        properties of an object that are None at the time of serialization.

        :param version: The version to which the object must be serialized to.
        This will default to the latest version supported by the library.
        :type version: str | unicode

        """
        return json.dumps(self.as_version(version))


    def as_version(self, version=Version.latest):