diff --git a/src/caosadvancedtools/table_json_conversion/xlsx_utils.py b/src/caosadvancedtools/table_json_conversion/xlsx_utils.py
index 41758907a21353c751272170bddaca6d0b8fb4c7..206e8e0845d64c2961812b11113a5557fec8b5dc 100644
--- a/src/caosadvancedtools/table_json_conversion/xlsx_utils.py
+++ b/src/caosadvancedtools/table_json_conversion/xlsx_utils.py
@@ -18,13 +18,25 @@
 # You should have received a copy of the GNU Affero General Public License
 # along with this program. If not, see <https://www.gnu.org/licenses/>.
 
-"""General utilities to work with XLSX files with (hidden) column and row annotations and typing."""
+"""General utilities to work with XLSX files with (hidden) column and row annotations and typing.
+
+The most prominent functions are:
+
+- ``p2s``: Path to string: ``["some", "path"] -> "some.path"``
+- ``read_or_dict``: Load JSON object from path, file or dict.
+
+This module also defines these enums:
+
+- ColumnType
+- RowType
+"""
 
 from __future__ import annotations
 
 import json
 
 from collections import OrderedDict
+from copy import deepcopy
 from enum import Enum
 from types import SimpleNamespace
 from typing import Dict, List, TextIO, Union
@@ -49,27 +61,9 @@ class RowType(Enum):
     IGNORE = 3
 
 
-def p2s(path: List[str]) -> str:
-    """Path to string: dot-separated.
-    """
-    return ".".join(path)
 
 
-def read_or_dict(data: Union[dict, str, TextIO]) -> dict:
-    """If data is a json file name or input stream, read data from there.
-If it is a dict already, just return it."""
-    if isinstance(data, dict):
-        return data
 
-    if isinstance(data, str):
-        with open(data, encoding="utf-8") as infile:
-            data = json.load(infile)
-    elif hasattr(data, "read"):
-        data = json.load(data)
-    else:
-        raise ValueError(f"I don't know how to handle the datatype of `data`: {type(data)}")
-    assert isinstance(data, dict)
-    return data
 
 
 def get_defining_paths(workbook: Workbook) -> dict[str, list[list[str]]]:
@@ -243,6 +237,16 @@ def get_worksheet_for_path(path: list[str], defining_path_index: dict[str, list[
     raise KeyError(f"Could not find defining worksheet for path: {path}")
 
 
+def is_exploded_sheet(sheet: Worksheet) -> bool:
+    """Return True if this is a an "exploded" sheet.
+
+    An exploded sheet is a sheet whose data entries are LIST valued properties of entries in another
+    sheet.  A sheet is detected as exploded iff it has FOREIGN columns.
+    """
+    column_types = _get_column_types(sheet)
+    return ColumnType.FOREIGN.name in column_types.values()
+
+
 def next_row_index(sheet: Worksheet) -> int:
     """Return the index for the next data row.
 
@@ -251,14 +255,27 @@ def next_row_index(sheet: Worksheet) -> int:
     return sheet.max_row
 
 
-def is_exploded_sheet(sheet: Worksheet) -> bool:
-    """Return True if this is a an "exploded" sheet.
-
-    An exploded sheet is a sheet whose data entries are LIST valued properties of entries in another
-    sheet.  A sheet is detected as exploded iff it has FOREIGN columns.
+def p2s(path: List[str]) -> str:
+    """Path to string: dot-separated.
     """
-    column_types = _get_column_types(sheet)
-    return ColumnType.FOREIGN.name in column_types.values()
+    return ".".join(path)
+
+
+def read_or_dict(data: Union[dict, str, TextIO]) -> dict:
+    """If data is a json file name or input stream, read data from there.
+If it is a dict already, just return it."""
+    if isinstance(data, dict):
+        return data
+
+    if isinstance(data, str):
+        with open(data, encoding="utf-8") as infile:
+            data = json.load(infile)
+    elif hasattr(data, "read"):
+        data = json.load(data)
+    else:
+        raise ValueError(f"I don't know how to handle the datatype of `data`: {type(data)}")
+    assert isinstance(data, dict)
+    return data
 
 
 def _get_column_types(sheet: Worksheet) -> OrderedDict: