Skip to content

xcast - Safe Type Conversions

Safe type conversion functions that return None on failure instead of raising exceptions.


xpytools.xtype.xcast.as_int

Python
as_int(value: Any, safe: bool = True) -> Optional[int]

Convert a value to int, returning None if unsafe or invalid.

Source code in xpytools/xtype/xcast/primitives.py
Python
def as_int(value: Any, safe: bool = True) -> Optional[int]:
    """Convert a value to int, returning None if unsafe or invalid."""
    if value is None:
        return None

    try:
        if is_int(value):
            return int(value)
        if is_float(value):
            return int(float(value))
        if is_str(value, non_empty=True) and is_int(value, allow_str=True):
            return int(value)
        if is_bool(value):
            return int(value)
        raise ValueError("Invalid integer value")
    except Exception:
        if not safe:
            raise
        return None

xpytools.xtype.xcast.as_float

Python
as_float(value: Any, safe: bool = True) -> Optional[float]

Convert a value to float, returning None if invalid.

Source code in xpytools/xtype/xcast/primitives.py
Python
def as_float(value: Any, safe: bool = True) -> Optional[float]:
    """Convert a value to float, returning None if invalid."""
    if value is None:
        return None

    try:
        if is_float(value):
            return float(value)
        if is_int(value):
            return float(value)
        if is_str(value, non_empty=True) and is_float(value, allow_str=True):
            return float(value)
        raise ValueError("Invalid float value")
    except Exception:
        if not safe:
            raise
        return None

xpytools.xtype.xcast.as_bool

Python
as_bool(value: Any, safe: bool = True) -> Optional[bool]

Convert a value to bool, handling numeric and string truthy values.

Source code in xpytools/xtype/xcast/primitives.py
Python
def as_bool(value: Any, safe: bool = True) -> Optional[bool]:
    """Convert a value to bool, handling numeric and string truthy values."""
    if value is None:
        return None
    try:
        if is_bool(value):
            return bool(value)
        if is_int(value) or is_float(value):
            return float(value) != 0.0
        if is_str(value, non_empty=True):
            v = value.strip().lower()
            if v in {"true", "yes", "1", "on"}:
                return True
            if v in {"false", "no", "0", "off"}:
                return False
        raise ValueError("Invalid boolean value")
    except Exception:
        if not safe:
            raise
        return bool(value)

xpytools.xtype.xcast.as_str

Python
as_str(value: Any, safe: bool = True, encoding: str = 'utf-8', errors: str = 'ignore') -> Optional[str]

Convert any object to a safe string.

  • datetime → ISO string (via as_datetime_str)
  • dict/list → JSON string (via as_json_str)
  • UUID-like → canonical UUID string
  • bytes/bytearray → decoded using UTF-8 (default)
  • everything else → str(value)

Parameters:

Name Type Description Default

value

Any

Input to convert.

required

safe

bool

Swallow conversion errors if True.

True

encoding

str

Encoding used for byte-to-string decoding.

"utf-8"

errors

str

Error handling strategy for decoding.

"ignore"

Returns:

Type Description
str | None

String representation or None if conversion fails.

Examples:

Python Console Session
>>> as_str(b"abc")
'abc'
>>> as_str({"x": 1})
'{\n  "x": 1\n}'
>>> as_str(datetime(2025,1,1))
'2025-01-01T00:00:00+00:00'
>>> as_str(None)
None
Source code in xpytools/xtype/xcast/primitives.py
Python
def as_str(
        value: Any,
        safe: bool = True,
        encoding: str = "utf-8",
        errors: str = "ignore",
        ) -> Optional[str]:
    """
    Convert any object to a safe string.

    - datetime → ISO string (via as_datetime_str)
    - dict/list → JSON string (via as_json_str)
    - UUID-like → canonical UUID string
    - bytes/bytearray → decoded using UTF-8 (default)
    - everything else → str(value)

    Parameters
    ----------
    value : Any
        Input to convert.
    safe : bool, default=True
        Swallow conversion errors if True.
    encoding : str, default="utf-8"
        Encoding used for byte-to-string decoding.
    errors : str, default="ignore"
        Error handling strategy for decoding.

    Returns
    -------
    str | None
        String representation or None if conversion fails.

    Examples
    --------
    >>> as_str(b"abc")
    'abc'
    >>> as_str({"x": 1})
    '{\\n  "x": 1\\n}'
    >>> as_str(datetime(2025,1,1))
    '2025-01-01T00:00:00+00:00'
    >>> as_str(None)
    None
    """
    if value is None:
        return None

    try:
        # Specialized types
        if is_datetime_like(value):
            return as_datetime_str(value)
        if is_json_like(value):
            return as_json_str(value)
        if is_uuid(value):
            return str(value)

        # Decode bytes safely
        if isinstance(value, (bytes, bytearray)):
            try:
                return value.decode(encoding, errors=errors)
            except Exception:
                # fallback: repr-based safe string
                return repr(value) if safe else value.decode(encoding)

        # Pass-through for strings
        if is_str(value):
            return value

        # Fallback to default str() conversion
        raise ValueError("Invalid string value")

    except Exception:
        if not safe:
            raise
        return str(value)

xpytools.xtype.xcast.as_bytes

Python
as_bytes(value: Any, safe: bool = True, encoding: str = 'utf-8') -> Optional[bytes]

Safely encode any value as bytes.

Behavior: - str → encoded with UTF-8 - dict/list → JSON-encoded (via as_json_str) - everything else → coerced via str()

Parameters:

Name Type Description Default

value

Any

Input to encode.

required

encoding

str

Encoding used for string conversion.

"utf-8"

safe

bool

Swallow encoding errors if True.

True

Returns:

Type Description
bytes | None

Encoded bytes or None on failure.

Examples:

Python Console Session
>>> as_bytes("abc")
b'abc'
>>> as_bytes({"a": 1})
b'{\n  "a": 1\n}'
Source code in xpytools/xtype/xcast/primitives.py
Python
def as_bytes(value: Any, safe: bool = True, encoding: str = "utf-8") -> Optional[bytes]:
    """
    Safely encode any value as bytes.

    Behavior:
      - str → encoded with UTF-8
      - dict/list → JSON-encoded (via `as_json_str`)
      - everything else → coerced via str()

    Parameters
    ----------
    value : Any
        Input to encode.
    encoding : str, default="utf-8"
        Encoding used for string conversion.
    safe : bool, default=True
        Swallow encoding errors if True.

    Returns
    -------
    bytes | None
        Encoded bytes or None on failure.

    Examples
    --------
    >>> as_bytes("abc")
    b'abc'
    >>> as_bytes({"a": 1})
    b'{\\n  "a": 1\\n}'
    """
    if value is None:
        return None

    try:
        if isinstance(value, bytes):
            return value
        if isinstance(value, str):
            return value.encode(encoding, errors="ignore")
        json_str = as_json_str(value)
        if json_str is not None:
            return json_str.encode(encoding, errors="ignore")
        raise ValueError("Invalid bytes value")
    except Exception:
        if not safe:
            raise
        return str(value).encode(encoding, errors="ignore")

xpytools.xtype.xcast.as_json

Python
as_json(value: Any, safe: bool = True) -> Optional[Union[dict, list]]

Convert JSON string or compatible Python object to dict/list.

Source code in xpytools/xtype/xcast/json.py
Python
def as_json(value: Any, safe: bool = True) -> Optional[Union[dict, list]]:
    """Convert JSON string or compatible Python object to dict/list."""
    if value is None:
        return None

    try:
        if is_json_like(value):
            if is_json(value):
                return value
            if is_str(value, non_empty=True):
                return _as_json_obj(value, safe)
        raise ValueError(f"Invalid JSON value: {value}")
    except Exception:
        if not safe:
            raise
        return None

xpytools.xtype.xcast.as_json_str

Python
as_json_str(value: Any, indent: int = 2, sort_keys: bool = False, safe: bool = True) -> Optional[str]

Safely serialize Python object to JSON string.

Returns None if object cannot be serialized.

Source code in xpytools/xtype/xcast/json.py
Python
def as_json_str(value: Any, indent: int = 2, sort_keys: bool = False, safe: bool = True) -> Optional[str]:
    """
    Safely serialize Python object to JSON string.

    Returns None if object cannot be serialized.
    """
    try:
        return json.dumps(value, indent=indent, sort_keys=sort_keys, default=str)
    except Exception:
        if safe:
            return None
        else:
            raise

xpytools.xtype.xcast.as_dict

Python
as_dict(value: Any, safe: bool = True) -> Optional[dict]

Safely convert input into a Python dict.

This uses as_json() internally, which supports: - JSON strings - Python dicts - List-like inputs (which will be ignored if not dict-like)

Parameters:

Name Type Description Default

value

Any

Input value to convert.

required

safe

bool

Swallow conversion errors if True.

True

Returns:

Type Description
dict | None

A dictionary if successfully converted, otherwise None.

Examples:

Python Console Session
>>> as_dict('{"a":1}')
{'a': 1}
>>> as_dict({"b": 2})
{'b': 2}
>>> as_dict('[1,2,3]')
None
Source code in xpytools/xtype/xcast/complex.py
Python
def as_dict(value: Any, safe: bool = True) -> Optional[dict]:
    """
    Safely convert input into a Python dict.

    This uses `as_json()` internally, which supports:
      - JSON strings
      - Python dicts
      - List-like inputs (which will be ignored if not dict-like)

    Parameters
    ----------
    value : Any
        Input value to convert.
    safe : bool, default=True
        Swallow conversion errors if True.

    Returns
    -------
    dict | None
        A dictionary if successfully converted, otherwise None.

    Examples
    --------
    >>> as_dict('{"a":1}')
    {'a': 1}
    >>> as_dict({"b": 2})
    {'b': 2}
    >>> as_dict('[1,2,3]')
    None
    """
    try:
        parsed = as_json(value)
        if isinstance(parsed, dict):
            return parsed
        return None
    except Exception:
        if not safe:
            raise
        return None

xpytools.xtype.xcast.as_list

Python
as_list(value: Any, safe: bool = True, wrap_scalar: bool = True) -> Optional[list]

Safely convert input into a Python list.

This uses as_json() internally, which supports: - JSON arrays (e.g. '[1, 2, 3]') - List/tuple/set - Scalars (optionally wrapped in a list)

Parameters:

Name Type Description Default

value

Any

Input value to convert.

required

safe

bool

Swallow conversion errors if True.

True

wrap_scalar

bool

Wrap single non-list values in a list for convenience.

True

Returns:

Type Description
list | None

A list if successfully converted, otherwise None.

Examples:

Python Console Session
>>> as_list('[1, 2, 3]')
[1, 2, 3]
>>> as_list((1, 2))
[1, 2]
>>> as_list('scalar', wrap_scalar=True)
['scalar']
Source code in xpytools/xtype/xcast/complex.py
Python
def as_list(value: Any, safe: bool = True, wrap_scalar: bool = True) -> Optional[list]:
    """
    Safely convert input into a Python list.

    This uses `as_json()` internally, which supports:
      - JSON arrays (e.g. '[1, 2, 3]')
      - List/tuple/set
      - Scalars (optionally wrapped in a list)

    Parameters
    ----------
    value : Any
        Input value to convert.
    safe : bool, default=True
        Swallow conversion errors if True.
    wrap_scalar : bool, default=True
        Wrap single non-list values in a list for convenience.

    Returns
    -------
    list | None
        A list if successfully converted, otherwise None.

    Examples
    --------
    >>> as_list('[1, 2, 3]')
    [1, 2, 3]
    >>> as_list((1, 2))
    [1, 2]
    >>> as_list('scalar', wrap_scalar=True)
    ['scalar']
    """
    try:
        parsed = as_json(value)
        if isinstance(parsed, list):
            return parsed
        if isinstance(value, (list, tuple, set)):
            return list(value)
        if wrap_scalar and value is not None:
            return [value]
        raise ValueError(f"Invalid JSON value: {value}")
    except Exception:
        if not safe:
            raise
        return None

xpytools.xtype.xcast.as_datetime

Python
as_datetime(value: Any, safe: bool = True, assume_tz_utc: bool = True) -> Optional[datetime]

Convert string, timestamp, or datetime-like value to datetime.

Source code in xpytools/xtype/xcast/datetime.py
Python
def as_datetime(value: Any, safe: bool = True, assume_tz_utc: bool = True) -> Optional[datetime]:
    """Convert string, timestamp, or datetime-like value to datetime."""
    try:
        if is_datetime(value):
            return value
        if is_datetime_like(value):
            return _to_datetime(value, assume_tz_utc)
        if is_int(value) or is_float(value):
            return _to_datetime(float(value), assume_tz_utc)
        raise ValueError(f"Invalid datetime value: {value}")

    except Exception:
        if not safe:
            raise
        return None

xpytools.xtype.xcast.as_datetime_str

Python
as_datetime_str(value: datetime, include_time: bool = True, include_utc: bool = False) -> Optional[str]

Safely convert a datetime object into an ISO-like string.

This function gracefully handles None, naive datetimes, and timezone-aware datetimes. It is designed for display or JSON serialization, providing consistent formatting options.

Parameters:

Name Type Description Default

value

datetime

Datetime object to format.

required

include_time

bool

Whether to include time components (HH:MM:SS).

True

include_utc

bool

Whether to include timezone info (if present or assume UTC).

True

Returns:

Type Description
str | None

Formatted datetime string, or None if input is invalid.

Examples:

Python Console Session
>>> from datetime import datetime, timezone
>>> as_datetime_str(datetime(2025, 1, 1, 12, 30, tzinfo=timezone.utc))
'2025-01-01T12:30:00+00:00'
Python Console Session
>>> as_datetime_str(datetime(2025, 1, 1),include_utc=False)
'2025-01-01T00:00:00'
Python Console Session
>>> as_datetime_str(None)
None
Source code in xpytools/xtype/xcast/datetime.py
Python
def as_datetime_str(value: datetime, include_time: bool = True, include_utc: bool = False) -> Optional[str]:
    """
    Safely convert a datetime object into an ISO-like string.

    This function gracefully handles None, naive datetimes, and timezone-aware
    datetimes. It is designed for display or JSON serialization, providing
    consistent formatting options.

    Parameters
    ----------
    value : datetime
        Datetime object to format.
    include_time : bool, default=True
        Whether to include time components (HH:MM:SS).
    include_utc : bool, default=True
        Whether to include timezone info (if present or assume UTC).

    Returns
    -------
    str | None
        Formatted datetime string, or None if input is invalid.

    Examples
    --------
    >>> from datetime import datetime, timezone
    >>> as_datetime_str(datetime(2025, 1, 1, 12, 30, tzinfo=timezone.utc))
    '2025-01-01T12:30:00+00:00'

    >>> as_datetime_str(datetime(2025, 1, 1),include_utc=False)
    '2025-01-01T00:00:00'

    >>> as_datetime_str(None)
    None
    """
    value = as_datetime(value, safe = True, assume_tz_utc = include_utc)

    if not is_datetime(value):
        return None

    try:
        # Ensure timezone if requested
        dt = value
        if include_utc and dt.tzinfo is None:
            dt = dt.replace(tzinfo=timezone.utc)
        elif not include_utc and dt.tzinfo is not None:
            dt = dt.replace(tzinfo=None)

        if include_time:
            fmt = "%Y-%m-%dT%H:%M:%S"
        else:
            fmt = "%Y-%m-%d"

        # Append timezone offset if required
        if include_utc:
            fmt += "%z"

        result = dt.strftime(fmt)

        # Normalize "+0000" → "+00:00"
        if include_utc and result.endswith("+0000"):
            result = result[:-5] + "+00:00"

        return result
    except Exception:
        return None

xpytools.xtype.xcast.as_df

Python
as_df(value: Any, safe: bool = True) -> Optional['pdDataFrame']

Safely convert various input types into a pandas DataFrame.

This function supports: - Already a DataFrame → returns as-is - Dict, list of dicts, or JSON string → converts via pandas.DataFrame - Returns None for invalid or unconvertible inputs - Returns None if pandas is not installed (instead of raising ImportError)

Parameters:

Name Type Description Default

value

Any

Input value to convert (DataFrame, dict, list, JSON string, etc.)

required

safe

bool

If True, swallows conversion errors and returns None instead of raising.

True

Returns:

Type Description
Optional[DataFrame]

A pandas DataFrame if conversion succeeds, otherwise None.

Examples:

Python Console Session
>>> import pandas as pd
>>> as_df(pd.DataFrame({'x':[1,2]}))
x
0  1
1  2
>>> as_df('[{"a":1},{"a":2}]')
   a
0  1
1  2
>>> as_df({'a':[1,2,3]})
   a
0  1
1  2
2  3
>>> as_df("bad json") is None
True
Source code in xpytools/xtype/xcast/dataframe.py
Python
@requireModules(["pandas"], exc_raise=True)
def as_df(value: Any, safe: bool = True) -> Optional["pdDataFrame"]:
    """
    Safely convert various input types into a pandas DataFrame.

    This function supports:
      - Already a DataFrame → returns as-is
      - Dict, list of dicts, or JSON string → converts via pandas.DataFrame
      - Returns None for invalid or unconvertible inputs
      - Returns None if pandas is not installed (instead of raising ImportError)

    Parameters
    ----------
    value : Any
        Input value to convert (DataFrame, dict, list, JSON string, etc.)
    safe : bool, default=True
        If True, swallows conversion errors and returns None instead of raising.

    Returns
    -------
    Optional[pandas.DataFrame]
        A pandas DataFrame if conversion succeeds, otherwise None.

    Examples
    --------
    >>> import pandas as pd
    >>> as_df(pd.DataFrame({'x':[1,2]}))
    x
    0  1
    1  2
    >>> as_df('[{"a":1},{"a":2}]')
       a
    0  1
    1  2
    >>> as_df({'a':[1,2,3]})
       a
    0  1
    1  2
    2  3
    >>> as_df("bad json") is None
    True
    """
    try:
        # Import inside function to avoid hard pandas dependency
        from pandas import DataFrame as pdDataFrame

        # Already a DataFrame → return as-is
        if is_df(value):
            return value

        # JSON-like (dict, list, or valid JSON string)
        if is_json_like(value):
            parsed = as_json(value)
            if parsed is None:
                raise ValueError("Invalid JSON value")
            return pdDataFrame(parsed)

        # Try generic coercion (lists of tuples, numpy arrays, etc.)
        try:
            return pdDataFrame(value)
        except Exception:
            raise

    except Exception:
        if not safe:
            raise
        return None

xpytools.xtype.xcast.as_none

Python
as_none(value: Any, safe: bool = True) -> Optional[Any]

Normalize any "null-like" value into Python None.

This function uses is_none() internally to detect: - None - NaN (float or numpy) - pandas.NA / pandas.NaT - string representations ("null", "nan", "n/a", "")

If the value is not considered null-like, it is returned unchanged.

Parameters:

Name Type Description Default

value

Any

Input value to normalize.

required

safe

bool

If False, re-raises unexpected exceptions.

True

Returns:

Type Description
Any | None

None if input is null-like, otherwise original value.

Examples:

Python Console Session
>>> as_none("NaN")
None
>>> as_none(pd.NA)
None
>>> as_none(123)
123
Source code in xpytools/xtype/xcast/null.py
Python
def as_none(value: Any, safe: bool = True) -> Optional[Any]:
    """
    Normalize any "null-like" value into Python None.

    This function uses `is_none()` internally to detect:
      - None
      - NaN (float or numpy)
      - pandas.NA / pandas.NaT
      - string representations ("null", "nan", "n/a", "")

    If the value is not considered null-like, it is returned unchanged.

    Parameters
    ----------
    value : Any
        Input value to normalize.
    safe : bool, default=True
        If False, re-raises unexpected exceptions.

    Returns
    -------
    Any | None
        None if input is null-like, otherwise original value.

    Examples
    --------
    >>> as_none("NaN")
    None
    >>> as_none(pd.NA)
    None
    >>> as_none(123)
    123
    """
    try:
        if is_none(value):
            return None
        else:
            return value
    except Exception:
        if not safe:
            raise
        return None

xpytools.xtype.xcast.to_primitives.to_primitives

Python
to_primitives(obj: Any) -> Any

Recursively coerce obj into a JSON-serializable structure.

Behavior
  • Dataclasses → dict
  • Pydantic models → dict (handles both v1 & v2)
  • pandas.DataFrame → list[dict]
  • pandas.Series → list
  • NumPy scalars / arrays → native Python types
  • Enum → enum.value
  • NaN / pd.NA / None-like → None
  • Nested containers handled recursively

Parameters:

Name Type Description Default

obj

Any

Input of arbitrary or nested type.

required

Returns:

Type Description
Any

JSON-safe structure: only dict, list, str, int, float, bool, or None.

Examples:

Python Console Session
>>> from dataclasses import dataclass
>>> import numpy as np, pandas as pd
>>> @dataclass
... class Example: x: int; y: float
>>> to_primitives(Example(1, np.nan))
{'x': 1, 'y': None}
>>> to_primitives(pd.DataFrame({'a':[1, None]}))
[{'a': 1}, {'a': None}]
>>> from xpyt_pydantic import BaseModel
>>> class M(BaseModel): a: int; b: float
>>> to_primitives(M(a=1, b=float("nan")))
{'a': 1, 'b': None}
Source code in xpytools/xtype/xcast/to_primitives.py
Python
def to_primitives(obj: Any) -> Any:
    """
    Recursively coerce `obj` into a JSON-serializable structure.

    Behavior
    --------
    - Dataclasses → dict
    - Pydantic models → dict (handles both v1 & v2)
    - pandas.DataFrame → list[dict]
    - pandas.Series → list
    - NumPy scalars / arrays → native Python types
    - Enum → enum.value
    - NaN / pd.NA / None-like → None
    - Nested containers handled recursively

    Parameters
    ----------
    obj : Any
        Input of arbitrary or nested type.

    Returns
    -------
    Any
        JSON-safe structure: only dict, list, str, int, float, bool, or None.

    Examples
    --------
    >>> from dataclasses import dataclass
    >>> import numpy as np, pandas as pd
    >>> @dataclass
    ... class Example: x: int; y: float
    >>> to_primitives(Example(1, np.nan))
    {'x': 1, 'y': None}
    >>> to_primitives(pd.DataFrame({'a':[1, None]}))
    [{'a': 1}, {'a': None}]
    >>> from xpyt_pydantic import BaseModel
    >>> class M(BaseModel): a: int; b: float
    >>> to_primitives(M(a=1, b=float("nan")))
    {'a': 1, 'b': None}
    """
    # --- Null / None-like ---------------------------------------------------
    if is_none(obj):
        return None

    # --- Dataclasses --------------------------------------------------------
    if is_dataclass(obj):
        return to_primitives(asdict(obj))

    # --- Pydantic models ----------------------------------------------------
    if pyd is not None:
        # Pydantic v1: BaseModel
        if hasattr(obj, "dict") and callable(getattr(obj, "dict", None)):
            try:
                return to_primitives(obj.dict())
            except Exception:
                pass
        # Pydantic v2: BaseModel.model_dump()
        if hasattr(obj, "model_dump") and callable(getattr(obj, "model_dump", None)):
            try:
                return to_primitives(obj.model_dump())
            except Exception:
                pass

    # --- Dicts --------------------------------------------------------------
    if is_dict(obj):
        return {k: to_primitives(v) for k, v in obj.items()}

    # --- Iterables / list-like ---------------------------------------------
    if is_list_like(obj):
        return [to_primitives(v) for v in obj]

    # --- Enum ---------------------------------------------------------------
    if isinstance(obj, Enum):
        return to_primitives(obj.value)

    # --- pandas / NumPy integration ----------------------------------------
    if is_df(obj):
        try:
            return to_primitives(
                    obj.replace({pd.NA: None, np.nan: None}).to_dict(orient="records")
                    )
        except Exception:
            return None

    if pd is not None and isinstance(obj, getattr(pd, "Series", ())):
        try:
            return to_primitives(obj.dropna().tolist())
        except Exception:
            return None

    if np is not None:
        # NumPy scalar
        if isinstance(obj, np.generic):
            return obj.item()
        # NumPy array
        if isinstance(obj, np.ndarray):
            try:
                return to_primitives(
                        np.where(pd.isna(obj), None, obj).tolist()
                        if pd is not None else obj.tolist()
                        )
            except Exception:
                return to_primitives(obj.tolist())

    # --- Primitive / fallback ----------------------------------------------
    if isinstance(obj, float) and np is not None and np.isnan(obj):
        return None
    if pd is not None and getattr(pd, "isna", None) and pd.isna(obj):
        return None

    if isinstance(obj, (str, int, float, bool)) or obj is None:
        return obj

    # --- Fallback -----------------------------------------------------------
    # Try to stringify remaining non-serializable types
    try:
        return str(obj)
    except Exception:
        return None