Repository: hed-standard/hed-python (language: python)

Issue:
Web work



Discussion / hints:

Fix patch:
diff --git a/hedtools/examples/run_hed_def_mapper_example.py b/hedtools/examples/run_hed_def_mapper_example.py
index e0ca47601..cef5926c4 100644
--- a/hedtools/examples/run_hed_def_mapper_example.py
+++ b/hedtools/examples/run_hed_def_mapper_example.py
@@ -11,8 +11,9 @@
           a HedSchema.
 """
 import os
+import hed
 from hed.util.event_file_input import EventFileInput
-from hed.util.hed_schema import HedSchema
+from hed import schema
 from hed.validator.hed_validator import HedValidator
 from hed.util.column_def_group import ColumnDefGroup
 
@@ -21,13 +22,13 @@
     example_data_path = 'data'  # path to example data
     hed3_tags_single_sheet = os.path.join(example_data_path, 'hed_tag_def_example.xlsx')
 
-    hed_schema = HedSchema(local_hed_file)
+    hed_schema = schema.load_schema(local_hed_file)
     prefixed_needed_tag_columns = {2: 'Event/Label/', 3: 'Event/Description/'}
     json_file = "data/both_types_events_def_example.json"
     column_group = ColumnDefGroup(json_file)
     def_dict, def_issues = column_group.extract_defs(hed_schema)
     if def_issues:
-        print(HedValidator.get_printable_issue_string(def_issues,
+        print(hed.get_printable_issue_string(def_issues,
                                                       title="There should be no errors in the definitions from the sidecars:"))
     input_file = EventFileInput(hed3_tags_single_sheet, json_def_files=column_group,
                                 tag_columns=[4], column_prefix_dictionary=prefixed_needed_tag_columns,
@@ -37,11 +38,11 @@
 
     validation_issues = input_file.validate_file_sidecars(hed_schema=hed_schema)
     if validation_issues:
-        print(HedValidator.get_printable_issue_string(validation_issues,
+        print(hed.get_printable_issue_string(validation_issues,
                                                       title="There should be no errors with the sidecar.  This will likely cause other errors if there are."))
     validator = HedValidator(hed_schema=hed_schema)
     validation_issues = validator.validate_input(input_file)
-    print(validator.get_printable_issue_string(validation_issues,
+    print(hed.get_printable_issue_string(validation_issues,
                                                "Normal hed string errors"))
 
     input_file.save(include_formatting=True, add_suffix="_test_output", output_processed_file=False)
diff --git a/hedtools/examples/run_hed_files_examples.py b/hedtools/examples/run_hed_files_examples.py
index b110181f5..1919d66a1 100644
--- a/hedtools/examples/run_hed_files_examples.py
+++ b/hedtools/examples/run_hed_files_examples.py
@@ -9,6 +9,7 @@
 """
 
 import os
+import hed
 
 from hed.validator.hed_validator import HedValidator
 from hed.util.hed_file_input import HedFileInput
@@ -32,19 +33,19 @@
     print(valid_tsv_file)
     input_file = HedFileInput(valid_tsv_file, tag_columns=[2])
     validation_issues = hed_validator_old.validate_input(input_file)
-    print(HedValidator.get_printable_issue_string(validation_issues, title='[Example 1a] ValidTwoColumnHED7_1_1 is probably okay with default version of HED'))
+    print(hed.get_printable_issue_string(validation_issues, title='[Example 1a] ValidTwoColumnHED7_1_1 is probably okay with default version of HED'))
 
     # Example 1b: Valid TSV file with specified local version of HED
     print(valid_tsv_file)
     input_file = HedFileInput(valid_tsv_file, tag_columns=[2])
     validation_issues = hed_validator_local.validate_input(input_file)
-    print(HedValidator.get_printable_issue_string(validation_issues, title='[Example 1b] ValidTwoColumnHED7_1_1 should have no issues with local version 7.1.1'))
+    print(hed.get_printable_issue_string(validation_issues, title='[Example 1b] ValidTwoColumnHED7_1_1 should have no issues with local version 7.1.1'))
 
     # Example 1c: Valid TSV file with specified local version of HED and no column headers
     print(valid_tsv_file_no_header)
     input_file = HedFileInput(valid_tsv_file_no_header, has_column_names=False, tag_columns=[2, 3])
     validation_issues = hed_validator_local.validate_input(input_file)
-    print(HedValidator.get_printable_issue_string(validation_issues, title='[Example 1c] ValidTwoColumnHED7_1_1NoHeaders should have no issues with version 7.1.1'))
+    print(hed.get_printable_issue_string(validation_issues, title='[Example 1c] ValidTwoColumnHED7_1_1NoHeaders should have no issues with version 7.1.1'))
 
     # Example 1d: Valid TSV with separate columns for required fields
     print(valid_tsv_file_separate_cols)
@@ -52,7 +53,7 @@
     input_file = HedFileInput(valid_tsv_file_separate_cols, tag_columns=[6],
                               column_prefix_dictionary=prefixed_needed_tag_columns)
     validation_issues = hed_validator_local.validate_input(input_file)
-    print(HedValidator.get_printable_issue_string(validation_issues, title='[Example 1d] Valid TSV with required tags in separate columns'))
+    print(hed.get_printable_issue_string(validation_issues, title='[Example 1d] Valid TSV with required tags in separate columns'))
 
     # Example 2a: CSV files are not supported (Use excel to convert)
     print(unsupported_csv_format)
@@ -80,36 +81,36 @@
                               column_prefix_dictionary=prefixed_needed_tag_columns,
                               worksheet_name='LKT Events')
     validation_issues = hed_validator_local.validate_input(input_file)
-    print(HedValidator.get_printable_issue_string(validation_issues, title='[Example 3a] Multiple sheet xlsl has LKT Events sheet with no issues'))
+    print(hed.get_printable_issue_string(validation_issues, title='[Example 3a] Multiple sheet xlsl has LKT Events sheet with no issues'))
 
     # Example 3b: Valid XLSX file with multiple sheets - first sheet probably has no issues with default schema
     input_file = HedFileInput(multiple_sheet_xlsx_file, tag_columns=[4],
                               column_prefix_dictionary=prefixed_needed_tag_columns,
                               worksheet_name='LKT Events')
     validation_issues = hed_validator_local_warnings.validate_input(input_file)
-    print(HedValidator.get_printable_issue_string(validation_issues, title='[Example 3b] LKT Events sheet probably has with no issues with the default schema'))
+    print(hed.get_printable_issue_string(validation_issues, title='[Example 3b] LKT Events sheet probably has with no issues with the default schema'))
 
     # Example 3c: XLSX file with multiple sheets - assumes first sheet by default
     input_file = HedFileInput(multiple_sheet_xlsx_file, tag_columns=[4],
                               column_prefix_dictionary=prefixed_needed_tag_columns)
     validation_issues = hed_validator_local.validate_input(input_file)
-    print(HedValidator.get_printable_issue_string(validation_issues, title='[Example 3c] Multiple sheet xlsl has first sheet with no issues'))
+    print(hed.get_printable_issue_string(validation_issues, title='[Example 3c] Multiple sheet xlsl has first sheet with no issues'))
 
     # Example 3d: XLSX file with multiple sheets - PVT sheet has several issues with 7.1.1
     input_file = HedFileInput(multiple_sheet_xlsx_file, tag_columns=[4],
                               column_prefix_dictionary=prefixed_needed_tag_columns,
                               worksheet_name='PVT Events')
     validation_issues = hed_validator_local.validate_input(input_file)
-    print(HedValidator.get_printable_issue_string(validation_issues, title='[Example 3d] Some PVT worksheet issues that are due to removal of extensionAllowed in places in 7.1.1'))
+    print(hed.get_printable_issue_string(validation_issues, title='[Example 3d] Some PVT worksheet issues that are due to removal of extensionAllowed in places in 7.1.1'))
 
     # Example 3e: Invalid XLSX sheet with 7.1.1
     input_file = HedFileInput(multiple_sheet_xlsx_file, tag_columns=[4], worksheet_name='DAS Events')
     validation_issues = hed_validator_local.validate_input(input_file)
-    print(HedValidator.get_printable_issue_string(validation_issues, title='[Example 3e] DAS Events worksheet has multiple issues with 7.1.1'))
+    print(hed.get_printable_issue_string(validation_issues, title='[Example 3e] DAS Events worksheet has multiple issues with 7.1.1'))
 
     # Example 3f: Invalid XLSX sheet with 7.1.1 - also can't duplicate Label and Description in 7.1.1
     input_file = HedFileInput(multiple_sheet_xlsx_file, tag_columns=[4],
                               column_prefix_dictionary=prefixed_needed_tag_columns,
                               worksheet_name='DAS Events')
     validation_issues = hed_validator_local.validate_input(input_file)
-    print(HedValidator.get_printable_issue_string(validation_issues, title='[Example 3f] DAS worse now because of duplicate Label and Description specifications'))
+    print(hed.get_printable_issue_string(validation_issues, title='[Example 3f] DAS worse now because of duplicate Label and Description specifications'))
diff --git a/hedtools/examples/run_hed_string_examples.py b/hedtools/examples/run_hed_string_examples.py
index 73b3b8760..eb2fc81ab 100644
--- a/hedtools/examples/run_hed_string_examples.py
+++ b/hedtools/examples/run_hed_string_examples.py
@@ -5,6 +5,7 @@
 HedValidator - Validates a given input string or file
 """
 
+import hed
 from hed.validator.hed_validator import HedValidator
 
 if __name__ == '__main__':
@@ -19,22 +20,22 @@
                    'Event/Category/Participant response, ' \
                    '(Participant ~ Action/Button press/Keyboard ~ Participant/Effect/Body part/Arm/Hand/Finger)'
     validation_issues = hed_validator_old.validate_input(hed_string_1)
-    print(HedValidator.get_printable_issue_string(validation_issues, title='[Example 1a] hed_string_1 should have no issues with HEDv7.1.1'))
+    print(hed.get_printable_issue_string(validation_issues, title='[Example 1a] hed_string_1 should have no issues with HEDv7.1.1'))
 
     # Example 1b: Try with the latest version of HED.xml
     validation_issues = hed_validator_current.validate_input(hed_string_1)
-    print(HedValidator.get_printable_issue_string(validation_issues, title='[Example 1b] hed_string_1 has issues with the latest HED version'))
+    print(hed.get_printable_issue_string(validation_issues, title='[Example 1b] hed_string_1 has issues with the latest HED version'))
 
     # Example 2a: Invalid HED string (junk in last tag)
     hed_string_2 = 'Event/Category/Participant response,'  \
                    '(Participant ~ Action/Button press/Keyboard ~ Participant/Effect/Body part/Arm/Hand/Finger),' \
                    'dskfjkf/dskjdfkj/sdkjdsfkjdf/sdlfdjdsjklj'
     validation_issues = hed_validator_old.validate_input(hed_string_2)
-    print(HedValidator.get_printable_issue_string(validation_issues, title='[Example 2a] hed_string_2 has junk in the last tag'))
+    print(hed.get_printable_issue_string(validation_issues, title='[Example 2a] hed_string_2 has junk in the last tag'))
 
     # Example 2b: However HED string of Example 2 has valid syntax so syntactic validation works
     validation_issues = hed_validator_no_semantic.validate_input(hed_string_2)
-    print(HedValidator.get_printable_issue_string(validation_issues, title='[Example 2b] hed_string_2 is syntactically correct'))
+    print(hed.get_printable_issue_string(validation_issues, title='[Example 2b] hed_string_2 is syntactically correct'))
 
     # Example 3a: Invalid HED string
     hed_string_3 = 'Event/Description/The start of increasing the size of sector, Event/Label/Sector start, ' \
@@ -43,7 +44,7 @@
                    '(Item/2D shape/Ellipse/Circle, Attribute/Visual/Color / Red), Sensory presentation/Visual, ' \
                    'Participant/Efffectt/Visual, Participant/Effect/Cognitive/Target'
     validation_issues = hed_validator_current.validate_input(hed_string_3)
-    print(HedValidator.get_printable_issue_string(validation_issues, title='[Example 3a] hed_string_3 has missing comma so fails before Efffectt typo'))
+    print(hed.get_printable_issue_string(validation_issues, title='[Example 3a] hed_string_3 has missing comma so fails before Efffectt typo'))
 
     # Example 3b: Using issue list directly - issues are returned as a list of dictionaries
     # print('[Example 3b] hed_string_3 has ', len(validation_issues), ' issues\n')
@@ -54,4 +55,4 @@
                    '(Participant ~ Action/Button press/Keyboard ~ Participant/Effect/Body part/Arm/Hand/Finger), ' \
                    '(Participant ~ Action/Deny/Access ~ Item/Object/Person/IDHolder)'
     validation_issues = hed_validator_current.validate_input(hed_string_4)
-    print(HedValidator.get_printable_issue_string(validation_issues, title='[Example 4a] the ~ notation in hed_string_4 works in v7.1.1'))
+    print(hed.get_printable_issue_string(validation_issues, title='[Example 4a] the ~ notation in hed_string_4 works in v7.1.1'))
diff --git a/hedtools/examples/run_json_validation_and_conversion.py b/hedtools/examples/run_json_validation_and_conversion.py
index e8e13f8b0..e5cb11943 100644
--- a/hedtools/examples/run_json_validation_and_conversion.py
+++ b/hedtools/examples/run_json_validation_and_conversion.py
@@ -10,19 +10,20 @@
 TagFormat - Used to convert between short and long tags
 ColumnDefGroup - Contains the data from a single json sidecar, can be validated using a HedSchema.
 """
+import hed
 from hed.util.column_def_group import ColumnDefGroup
-from hed.util.hed_schema import HedSchema
+from hed import schema
 from hed.tools.tag_format import TagFormat
 
 local_hed_xml = "data/HED8.0.0-alpha.1.xml"
-hed_schema = HedSchema(local_hed_xml)
+hed_schema = schema.load_schema(local_hed_xml)
 json_filename = "data/both_types_events_errors.json"
 
 # Example 1
 json_file = ColumnDefGroup(json_filename)
 # Print all the errors from the json file
 errors = json_file.validate_entries(hed_schema)
-print(json_file.get_printable_issue_string(errors))
+print(hed.get_printable_issue_string(errors))
 
 # Example 2
 # Open the json file, convert all tags to long, and save it out
diff --git a/hedtools/examples/run_validate_schema.py b/hedtools/examples/run_validate_schema.py
index f072030eb..695f713b8 100644
--- a/hedtools/examples/run_validate_schema.py
+++ b/hedtools/examples/run_validate_schema.py
@@ -2,17 +2,19 @@
 Example of how to validate a given hed schema file.
 
 Functions Demonstrated:
-schema_validator.validate_schema - Takes a schema XML filename as input and returns all validation issues found within.
+hed_schema.check_compliance - Checks if a HedSchema object is hed3 compatible.
 """
-from hed.schema import schema_validator
+
+import hed
 
 if __name__ == '__main__':
     # This old schema should produce many issues, including many duplicate terms
     local_hed_xml = "data/HED7.1.1.xml"
-    issues = schema_validator.validate_schema(local_hed_xml)
-    print(schema_validator.get_printable_issue_string(issues))
+    hed_schema = hed.schema.load_schema(local_hed_xml)
+    issues = hed_schema.check_compliance()
+    print(hed.get_printable_issue_string(issues))
 
     # this should produce fairly minimal issues.
     local_hed_xml = "data/HED8.0.0-alpha.1.xml"
-    issues = schema_validator.validate_schema(local_hed_xml)
-    print(schema_validator.get_printable_issue_string(issues))
+    issues = hed_schema.check_compliance()
+    print(hed.get_printable_issue_string(issues))
diff --git a/hedtools/examples/run_wiki2xml.py b/hedtools/examples/run_wiki2xml.py
index e1cb57262..bf8ce9f0c 100644
--- a/hedtools/examples/run_wiki2xml.py
+++ b/hedtools/examples/run_wiki2xml.py
@@ -2,18 +2,18 @@
 Example of converting a hed schema from .mediawiki format to .xml format.
 
 Functions Demonstrated:
-wiki2xml.convert_hed_wiki_2_xml - Converts hed schema from given url or filename,
+schema.convert_schema_to_format - Converts hed schema from given url or filename,
                                     returns a temporary filename for the converted file and a list of issues.
-ErrorHandler.get_printable_issue_string - Static method that converts the list of issues to a human readable form.
+hed.get_printable_issue_string - method that converts the list of issues to a human readable form.
 """
 from shutil import move
-from hed.schema import wiki2xml
-from hed.util.error_reporter import ErrorHandler
+import hed
+from hed import schema
 
 if __name__ == '__main__':
     hed_wiki_url = 'https://raw.githubusercontent.com/hed-standard/hed-specification/master/HED-generation3-schema.mediawiki'
     local_hed_file = None
-    xml_location, errors = wiki2xml.convert_hed_wiki_2_xml(hed_wiki_url, local_wiki_file=local_hed_file)
-    ErrorHandler.get_printable_issue_string(validation_issues=errors, title="Errors in HED-generation3-schema.mediawiki")
+    xml_location, errors = schema.convert_schema_to_format(hed_wiki_url, local_hed_file=local_hed_file)
+    hed.get_printable_issue_string(validation_issues=errors, title="Errors in HED-generation3-schema.mediawiki")
     if xml_location:
         move(xml_location, "output.xml")
\ No newline at end of file
diff --git a/hedtools/examples/run_xml2wiki.py b/hedtools/examples/run_xml2wiki.py
index 637d161cb..3cebc7363 100644
--- a/hedtools/examples/run_xml2wiki.py
+++ b/hedtools/examples/run_xml2wiki.py
@@ -2,19 +2,20 @@
 Example of converting a hed schema from .xml format to .mediawiki format.
 
 Functions Demonstrated:
-wiki2xml.convert_hed_xml_2_wiki - Converts hed schema from given url or filename,
+wiki2xml.convert_schema_to_format - Converts hed schema from given url or filename,
                                     returns a temporary filename for the converted file and a list of issues.
-ErrorHandler.get_printable_issue_string - Static method that converts the list of issues to a human readable form.
+hed.get_printable_issue_string - method that converts the list of issues to a human readable form.
 """
 
 from shutil import move
-from hed.schema import xml2wiki
-from hed.util.error_reporter import ErrorHandler
+import hed
+from hed import schema
+
 
 if __name__ == '__main__':
     hed_xml_url = 'https://raw.githubusercontent.com/hed-standard/hed-specification/master/hedxml/HED7.1.1.xml'
     local_xml_file = None
-    mediawiki_location, errors = xml2wiki.convert_hed_xml_2_wiki(hed_xml_url, local_xml_file)
-    ErrorHandler.get_printable_issue_string(validation_issues=errors, title="Errors in HED7.1.1.xml")
+    mediawiki_location, errors = schema.convert_schema_to_format(hed_xml_url, local_xml_file, save_as_mediawiki=True)
+    hed.get_printable_issue_string(validation_issues=errors, title="Errors in HED7.1.1.xml")
     if mediawiki_location:
         move(mediawiki_location, "HED7.1.1.mediawiki")
diff --git a/hedtools/hed/__init__.py b/hedtools/hed/__init__.py
index e69de29bb..27f861bee 100644
--- a/hedtools/hed/__init__.py
+++ b/hedtools/hed/__init__.py
@@ -0,0 +1,2 @@
+from hed.util.error_reporter import get_printable_issue_string
+from hed import schema
diff --git a/hedtools/hed/schema/__init__.py b/hedtools/hed/schema/__init__.py
index e69de29bb..edd9b9628 100644
--- a/hedtools/hed/schema/__init__.py
+++ b/hedtools/hed/schema/__init__.py
@@ -0,0 +1,3 @@
+from hed.schema.hed_schema import HedSchema
+from hed.schema.hed_schema_file import load_schema, get_hed_xml_version, convert_schema_to_format
+from hed.schema.hed_schema_constants import HedKey
diff --git a/hedtools/hed/schema/constants.py b/hedtools/hed/schema/constants.py
deleted file mode 100644
index 6a0fba900..000000000
--- a/hedtools/hed/schema/constants.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# Valid top level file attributes in a HED xml file
-# eg .mediawiki version 'HED library:yourlibraryname, version:7.1.1'
-HED_VALID_ATTRIBUTES = ['library', 'version']
diff --git a/hedtools/hed/schema/hed_schema.py b/hedtools/hed/schema/hed_schema.py
new file mode 100644
index 000000000..2fea34d29
--- /dev/null
+++ b/hedtools/hed/schema/hed_schema.py
@@ -0,0 +1,401 @@
+"""
+This module contains the HedSchema class which encapsulates all HED tags, tag attributes, unit classes, and
+unit class attributes in a dictionary.
+
+The dictionary is a dictionary of dictionaries. The dictionary names are the list in HedKey from hed_schema_constants.
+"""
+from hed.schema.hed_schema_constants import HedKey
+from hed.schema import hed_schema_constants as constants
+from hed.util import file_util
+from hed.schema.schema2xml import HedSchema2XML
+from hed.schema.schema2wiki import HedSchema2Wiki
+from hed.schema import schema_compliance
+
+
+class HedSchema:
+    def __init__(self):
+        """Constructor for the HedSchema class.
+
+        Parameters
+        ----------
+        Returns
+        -------
+        HedSchema
+            A HedSchema object.
+        """
+        self.no_duplicate_tags = True
+        self.schema_attributes = {}
+        self._filename = None
+        self.dictionaries = self.create_empty_dictionaries()
+        self.prologue = ""
+        self.epilogue = ""
+
+        self.issues = []
+
+    def set_attributes(self, schema_attributes):
+        self.schema_attributes = schema_attributes
+
+    def set_dictionaries(self, dictionaries):
+        self.dictionaries = dictionaries
+        self._propagate_extension_allowed()
+        self._populate_short_tag_dict()
+        self._add_hed3_compatible_tags()
+
+    @property
+    def filename(self):
+        return self._filename
+
+    @filename.setter
+    def filename(self, value):
+        if self._filename is None:
+            self._filename = value
+
+    def save_as_xml(self):
+        schema2xml = HedSchema2XML()
+        xml_tree = schema2xml.process_schema(self)
+        local_xml_file = file_util.write_xml_tree_2_xml_file(xml_tree, ".xml")
+        return local_xml_file
+
+    def save_as_mediawiki(self):
+        schema2wiki = HedSchema2Wiki()
+        output_strings = schema2wiki.process_schema(self)
+        local_wiki_file = file_util.write_strings_to_file(output_strings, ".mediawiki")
+        return local_wiki_file
+
+    def __eq__(self, other):
+        if self.schema_attributes != other.schema_attributes:
+            return False
+        if self.schema_attributes != other.schema_attributes:
+            return False
+        if self.no_duplicate_tags != other.no_duplicate_tags:
+            return False
+        if self.prologue != other.prologue:
+            return False
+        if self.epilogue != other.epilogue:
+            return False
+        return True
+
+    @staticmethod
+    def create_empty_dictionaries():
+        """
+        Initializes a dictionary with the minimum so the tools won't crash
+        """
+        dictionaries = {}
+        for tag in constants.ALL_TAG_DICTIONARY_KEYS:
+            dictionaries[tag] = {}
+        dictionaries[HedKey.Descriptions] = {}
+        return dictionaries
+
+    def check_compliance(self, also_check_for_warnings=True, display_filename=None,
+                         error_handler=None):
+        """
+            Checks for hed3 compliance of this schema.
+
+        Parameters
+        ----------
+        also_check_for_warnings : bool, default True
+            If True, also checks for formatting issues like invalid characters, capitalization, etc.
+        display_filename: str
+            If present, will use this as the filename for context, rather than using the actual filename
+            Useful for temp filenames.
+        error_handler : ErrorHandler or None
+            Used to report errors.  Uses a default one if none passed in.
+        Returns
+        -------
+        issue_list : [{}]
+            A list of all warnings and errors found in the file.
+        """
+        return schema_compliance.check_compliance(self, also_check_for_warnings, display_filename, error_handler)
+
+    def dupe_tag_iter(self, return_detailed_info=False):
+        """
+        An iterator that goes over each line of the duplicate tags dict, including descriptive ones.
+
+        Parameters
+        ----------
+        return_detailed_info : bool
+            If true, also returns header lines listing the number of duplicate tags for each short tag.
+
+        Yields
+        -------
+        text_line: str
+            A list of long tags that have duplicates, with optional descriptive short tag lines.
+        """
+        duplicate_dict = self.find_duplicate_tags()
+        prefix_string = ""
+        if return_detailed_info:
+            prefix_string = "\t"
+        for tag_name in duplicate_dict:
+            if return_detailed_info:
+                yield f"Duplicate tag found {tag_name} - {len(duplicate_dict[tag_name])} versions:"
+            for tag_entry in duplicate_dict[tag_name]:
+                yield f"{prefix_string}{tag_entry}"
+
+    def find_duplicate_tags(self):
+        """Finds all tags that are not unique.
+
+        Returns
+        -------
+        duplicate_tag_dict: {str: [str]}
+            A dictionary of all duplicate short tags as keys, with the values being a list of
+            long tags sharing that short tag
+        """
+        duplicate_dict = {}
+        short_tag_dict = self.dictionaries[HedKey.ShortTags]
+        for tag_name in short_tag_dict:
+            if isinstance(short_tag_dict[tag_name], list):
+                duplicate_dict[tag_name] = short_tag_dict[tag_name]
+
+        return duplicate_dict
+
+    def get_desc_dict(self):
+        """
+            Helper to return HedKey.Descriptions dictionary
+
+        Returns
+        -------
+        descriptions_dict: {str:str}
+        """
+        return self.dictionaries[HedKey.Descriptions]
+
+    def get_tag_description(self, tag_name, tag_class=HedKey.AllTags):
+        """
+            If a description exists for the given name, returns it
+
+        Parameters
+        ----------
+        tag_name : str
+            A hed tag name(or unit/unit modifier etc) with proper capitalization.
+        tag_class: str, default HedKey.AllTags
+            A HedKey indicating what type of description you are asking for.  (All tags, Units, Unit modifier)
+
+        Returns
+        -------
+        description: str or None
+        """
+        if tag_class == HedKey.AllTags:
+            tag_class = ""
+        return self.dictionaries[HedKey.Descriptions].get(tag_class + tag_name, None)
+
+    def get_all_tags(self, return_short_form=False):
+        """
+        Gets a single copy of all hed terms from the schema, for hed2 or hed3 compatible.
+
+        Returns
+        -------
+        term_list: [str]
+            A list of all terms(short tags) from the schema.
+        """
+        final_list = []
+        if not self.has_duplicate_tags():
+            for lower_tag, org_tag in self.short_tag_mapping.items():
+                if return_short_form:
+                    final_list.append(org_tag.split('/')[-1])
+                else:
+                    final_list.append(org_tag)
+        # Fallback for hed2 style schema validation
+        else:
+            for lower_tag, org_tag in self.dictionaries[HedKey.AllTags].items():
+                if return_short_form:
+                    final_list.append(org_tag.split('/')[-1])
+                else:
+                    final_list.append(org_tag)
+        return final_list
+
+    def get_all_tag_attributes(self, tag_name, keys=None):
+        """
+            Gathers all attributes for a given tag name.  If keys is none, gets all normal hed tag attributes.
+
+        Parameters
+        ----------
+        tag_name : str
+            The name of the tag to check
+        keys : [str]
+            A list of HedKey tags.
+
+        Returns
+        -------
+        tag_values: {str: str}
+            {key_name : attribute_value}
+        """
+        if keys is None:
+            keys = constants.TAG_ATTRIBUTE_KEYS
+        attributes = {}
+        for key in keys:
+            check_name = tag_name
+            if key in constants.TAG_ATTRIBUTE_KEYS:
+                check_name = tag_name.lower()
+            if check_name in self.dictionaries[key]:
+                value = self.dictionaries[key][check_name]
+                # A tag attribute is considered True if the tag name and dictionary value are the same, ignoring capitalization
+                if value and check_name.lower() == value.lower():
+                    attributes[key] = True
+                else:
+                    if value is None:
+                        value = False
+                    attributes[key] = value
+
+        return attributes
+
+    def get_all_forms_of_tag(self, short_tag_to_check):
+        """
+        Given a short tag, return all the longer versions of it.  Always returns empty list if not a valid HED3G schema.
+
+        eg: "definition" will return
+                ["definition", "informational/definition", "attribute/informational/definition"]
+
+        Parameters
+        ----------
+        short_tag_to_check : str
+            The short version of a hed tag we are interested in.
+
+        Returns
+        -------
+        tag_versions: [str]
+            A list of all short, intermediate, and long versions of the passed in short tag.
+            Returns empty list if no versions found.
+        """
+        try:
+            tag_entry = self.short_tag_mapping[short_tag_to_check.lower()]
+        except (KeyError, TypeError):
+            return []
+
+        split_tags = tag_entry.lower().split("/")
+        final_tag = ""
+        all_forms = []
+        for tag in reversed(split_tags):
+            final_tag = tag + "/" + final_tag
+            all_forms.append(final_tag)
+
+        return all_forms
+
+    def has_duplicate_tags(self):
+        """
+        Returns True if this is a valid hed3 schema with no duplicate short tags.
+
+        Returns
+        -------
+        bool
+            Returns True if this is a valid hed3 schema with no duplicate short tags.
+        """
+        return not self.no_duplicate_tags
+
+    @property
+    def has_unit_classes(self):
+        return HedKey.Units in self.dictionaries
+
+    @property
+    def has_unit_modifiers(self):
+        return HedKey.SIUnitModifier in self.dictionaries
+
+    @property
+    def short_tag_mapping(self):
+        """
+        This returns the short->long tag dictionary if we have a hed3 compatible schema.
+
+        Returns
+        -------
+        short_tag_dict: {}
+            Returns the short tag mapping dictionary, or None if this is not a hed3 compatible schema.
+        """
+        if self.no_duplicate_tags:
+            return self.dictionaries[HedKey.ShortTags]
+        return None
+
+    def tag_has_attribute(self, tag, tag_attribute):
+        """Checks to see if the tag has a specific attribute.
+
+        Parameters
+        ----------
+        tag: str
+            A tag.
+        tag_attribute: str
+            A tag attribute.
+        Returns
+        -------
+        bool
+            True if the tag has the specified attribute. False, if otherwise.
+
+        """
+        if self.dictionaries[tag_attribute].get(tag.lower()):
+            return True
+        return False
+
+    def _propagate_extension_allowed(self):
+        """
+        Populates the ExtensionAllowedPropagated based on the ExtensionAllowed one.
+
+        Returns
+        -------
+
+        """
+        allowed_extensions = self.dictionaries[HedKey.ExtensionAllowed]
+        for long_tag in self.dictionaries[HedKey.AllTags].values():
+            lower_tag = long_tag.lower()
+            if lower_tag in allowed_extensions:
+                self.dictionaries[HedKey.ExtensionAllowedPropagated][lower_tag] = long_tag
+                continue
+
+            if self.tag_has_attribute(lower_tag, HedKey.TakesValue):
+                continue
+
+            current_index = -1
+            found_slash = lower_tag.find("/", current_index + 1)
+            while found_slash != -1:
+                current_index = found_slash
+                check_tag = lower_tag[:current_index]
+                if check_tag in allowed_extensions:
+                    self.dictionaries[HedKey.ExtensionAllowedPropagated][lower_tag] = long_tag
+                    break
+                found_slash = lower_tag.find("/", current_index + 1)
+
+    def _populate_short_tag_dict(self):
+        """
+        Create a mapping from the short version of a tag to the long version and
+        determines if this is a hed3 compatible schema.
+
+        Returns
+        -------
+        """
+        self.no_duplicate_tags = True
+        base_tag_dict = self.dictionaries[HedKey.AllTags]
+        new_short_tag_dict = {}
+        for tag, unformatted_tag in base_tag_dict.items():
+            split_tags = unformatted_tag.split("/")
+            short_tag = split_tags[-1]
+            if short_tag == "#":
+                # if it's a takes value tag, we should also include the parent.
+                short_tag = split_tags[-2] + "/#"
+            short_clean_tag = short_tag.lower()
+            new_tag_entry = unformatted_tag
+            if short_clean_tag not in new_short_tag_dict:
+                new_short_tag_dict[short_clean_tag] = new_tag_entry
+            else:
+                self.no_duplicate_tags = False
+                if not isinstance(new_short_tag_dict[short_clean_tag], list):
+                    new_short_tag_dict[short_clean_tag] = [new_short_tag_dict[short_clean_tag]]
+                new_short_tag_dict[short_clean_tag].append(new_tag_entry)
+        self.dictionaries[HedKey.ShortTags] = new_short_tag_dict
+
+    def _add_hed3_compatible_tags(self):
+        """
+        Updates the normal tag dictionaries with all the intermediate forms if this is a hed3 compatible schema."
+
+        Returns
+        -------
+        """
+        if self.no_duplicate_tags:
+            for dict_key in constants.ALL_TAG_DICTIONARY_KEYS:
+                tag_dictionary = self.dictionaries[dict_key]
+                new_entries = {}
+                for full_tag, value in tag_dictionary.items():
+                    split_tags = full_tag.split("/")
+                    final_tag = ""
+                    for tag in reversed(split_tags):
+                        final_tag = tag + "/" + final_tag
+                        # We need to include the #, but make sure we don't create a tag with just # alone.
+                        if tag == "#":
+                            continue
+                        # Remove extra trailing slash.
+                        new_entries[final_tag[:-1]] = value
+                tag_dictionary.update(new_entries)
diff --git a/hedtools/hed/schema/hed_schema_constants.py b/hedtools/hed/schema/hed_schema_constants.py
new file mode 100644
index 000000000..e2db575e3
--- /dev/null
+++ b/hedtools/hed/schema/hed_schema_constants.py
@@ -0,0 +1,55 @@
+# These need to match the attributes/element name/etc used to load from the xml
+class HedKey:
+    Default = 'default'
+    ExtensionAllowed = 'extensionAllowed'
+    # On opening, the extension allowed attribute is propagated down to child tags.
+    ExtensionAllowedPropagated = 'extensionAllowedPropagated'
+    IsNumeric = 'isNumeric'
+    Position = 'position'
+    PredicateType = 'predicateType'
+    Recommended = 'recommended'
+    RequiredPrefix = 'required'
+    RequireChild = 'requireChild'
+    AllTags = 'tags'
+    TakesValue = 'takesValue'
+    Unique = 'unique'
+    UnitClass = 'unitClass'
+
+    # Default Units for Type
+    DefaultUnits = 'defaultUnits'
+    Units = 'units'
+
+    # The next 5 are all case sensitive for the keys.
+    SIUnit = 'SIUnit'
+    UnitSymbol = 'unitSymbol'
+
+    SIUnitModifier = 'SIUnitModifier'
+    SIUnitSymbolModifier = 'SIUnitSymbolModifier'
+
+    # for normal tags, the key to this is the full tag name.
+    # For unit modifiers and such, prefix the tag with the appropriate HedKey.
+    # eg. dictionaries[HedKey.Descriptions][HedKey.SIUnitModifier + 'm']
+    Descriptions = 'descriptions'
+
+    # If this is a valid HED3 spec, this allows mapping from short to long.
+    ShortTags = 'shortTags'
+
+
+# List of all dictionary keys that are based off tag attributes.
+# These are the only ones directly loaded from or saved to files.
+TAG_ATTRIBUTE_KEYS = [HedKey.TakesValue, HedKey.IsNumeric, HedKey.Recommended,
+                      HedKey.RequireChild, HedKey.RequiredPrefix, HedKey.Unique, HedKey.PredicateType,
+                      HedKey.Position, HedKey.UnitClass, HedKey.Default, HedKey.ExtensionAllowed]
+# for these it gets the value of the attribute, rather than treating it as a bool.
+STRING_ATTRIBUTE_DICTIONARY_KEYS = [HedKey.Default, HedKey.UnitClass,
+                                    HedKey.Position, HedKey.PredicateType, HedKey.DefaultUnits]
+# List of all keys that are for tags, including ones derived from other attributes.
+ALL_TAG_DICTIONARY_KEYS = [*TAG_ATTRIBUTE_KEYS, HedKey.ExtensionAllowedPropagated, HedKey.AllTags]
+UNIT_CLASS_DICTIONARY_KEYS = [HedKey.SIUnit, HedKey.UnitSymbol]
+UNIT_MODIFIER_DICTIONARY_KEYS = [HedKey.SIUnitModifier, HedKey.SIUnitSymbolModifier]
+
+UNIT_CLASS_ATTRIBUTES = [HedKey.DefaultUnits, HedKey.SIUnit, HedKey.UnitSymbol]
+UNIT_MODIFIER_ATTRIBUTES = [HedKey.SIUnitModifier, HedKey.SIUnitSymbolModifier]
+
+VERSION_ATTRIBUTE = 'version'
+
diff --git a/hedtools/hed/schema/hed_schema_file.py b/hedtools/hed/schema/hed_schema_file.py
new file mode 100644
index 000000000..e92ff76f7
--- /dev/null
+++ b/hedtools/hed/schema/hed_schema_file.py
@@ -0,0 +1,80 @@
+from hed.schema.xml2schema import HedSchemaXMLParser
+from hed.schema.wiki2schema import HedSchemaWikiParser
+from hed.util.exceptions import HedFileError, HedExceptions
+from hed.schema import hed_schema_constants
+from hed.util import file_util
+
+
+def load_schema(hed_file_path):
+    if not hed_file_path:
+        raise HedFileError(HedExceptions.FILE_NOT_FOUND, "Empty file path passed to HedSchema.load_file", filename=hed_file_path)
+
+    if hed_file_path.lower().endswith(".xml"):
+        hed_schema = HedSchemaXMLParser.load_xml(hed_file_path)
+        return hed_schema
+    elif hed_file_path.lower().endswith(".mediawiki"):
+        hed_schema = HedSchemaWikiParser.load_wiki(hed_file_path)
+        return hed_schema
+    else:
+        raise HedFileError(HedExceptions.INVALID_EXTENSION, "Unknown schema extension", filename=hed_file_path)
+
+
+# todo: this could be updated to also support .mediawiki format.
+def get_hed_xml_version(hed_xml_file_path):
+    """Gets the version number from a HED XML file.
+
+    Parameters
+    ----------
+    hed_xml_file_path: str
+        The path to a HED XML file.
+    Returns
+    -------
+    str
+        The version number of the HED XML file.
+
+    """
+    root_node = HedSchemaXMLParser._parse_hed_xml_file(hed_xml_file_path)
+    return root_node.attrib[hed_schema_constants.VERSION_ATTRIBUTE]
+
+
+def convert_schema_to_format(hed_url=None, local_hed_file=None, check_for_issues=True,
+                             display_filename=None, save_as_mediawiki=False):
+    """
+    Loads a local schema file or from a URL, then outputs a temporary file with the requested format.
+
+    Parameters
+    ----------
+    hed_url: str or None
+        url pointing to the .xml/mediawiki file to use
+    local_hed_file: str or None
+        filepath to local xml/mediawiki hed schema(overrides hed_url)
+    check_for_issues : bool
+        After conversion checks for warnings like capitalization or invalid characters.
+    display_filename: str
+        If present, will use this as the filename for context, rather than using the actual filename
+        Useful for temp filenames.
+    save_as_mediawiki: bool
+        If True, save as .mediawiki.  if False, save as .xml
+    Returns
+    -------
+    output_filename: str
+        Location of output converted file, None on complete failure
+    issues_list: [{}]
+        returns a list of error/warning dictionaries
+    """
+    if local_hed_file is None:
+        local_hed_file = file_util.url_to_file(hed_url)
+
+    hed_schema = load_schema(local_hed_file)
+
+    issue_list = []
+    if check_for_issues:
+        warnings = hed_schema.check_compliance(display_filename=display_filename)
+        issue_list += warnings
+
+    if save_as_mediawiki:
+        output_filename = hed_schema.save_as_mediawiki()
+    else:
+        output_filename = hed_schema.save_as_xml()
+
+    return output_filename, issue_list
diff --git a/hedtools/hed/schema/parsetag.py b/hedtools/hed/schema/parsetag.py
deleted file mode 100644
index 77ad7fd6d..000000000
--- a/hedtools/hed/schema/parsetag.py
+++ /dev/null
@@ -1,280 +0,0 @@
-"""
-This module contains functions for parsing tags in a wiki HED schema.
-"""
-
-from xml.etree.ElementTree import SubElement
-import re
-
-attributes_expression = r'\{.*\}'
-description_expression = r'\[.*\]'
-extend_here_line = 'extend here'
-level_expression = r'\*+'
-unit_class_attribute = 'unitClass'
-no_wiki_tag = '</?nowiki>'
-square_bracket_removal_expression = r'[\[\]]'
-tag_name_element = 'name'
-invalid_characters_to_strip = ["&#8203;"]
-tag_name_regexp = r'([<>=#\-a-zA-Z0-9$:()\^µ]+\s*)+'
-tag_description_element = 'description'
-tag_element = 'node'
-true_attribute = 'true'
-unit_class_element = 'unitClass'
-unit_class_name_element = 'name'
-unit_class_units_element = 'units'
-unit_class_unit_element = 'unit'
-unit_modifier_element = 'unitModifier'
-unit_modifier_name_element = 'name'
-unit_modifier_description_element = 'description'
-
-
-def remove_nowiki_tag_from_line(tag_line):
-    """Removes the nowiki tag from the  line.
-
-    Parameters
-    ----------
-    tag_line: string
-        A tag line.
-
-    Returns
-    -------
-    string
-        The line with the nowiki tag removed.
-    """
-    tag_line = re.sub(no_wiki_tag, '', tag_line)
-    return tag_line
-
-
-def get_change_log_entry(change_log_line):
-    """Gets the change log entry from the line.
-
-    Parameters
-    ----------
-    change_log_line: string
-        line containing a change log entry.
-
-    Returns
-    -------
-    string
-        The change log entry.
-    """
-    name = re.compile('[^*]+$')
-    match = name.search(change_log_line)
-    if match:
-        return match.group().strip()
-    else:
-        return ''
-
-
-def get_tag_name(tag_line):
-    """Gets the tag name from the tag line.
-
-    Parameters
-    ----------
-    tag_line: string
-        A tag line.
-
-    Returns
-    -------
-    string
-        The tag name.
-    """
-    if tag_line.find(extend_here_line) != -1:
-        return ''
-    name = re.compile(tag_name_regexp)
-    for invalid_chars in invalid_characters_to_strip:
-        tag_line = tag_line.replace(invalid_chars, "")
-    match = name.search(tag_line)
-    if match:
-        return match.group().strip()
-    else:
-        return ''
-
-
-def get_tag_attributes(tag_line):
-    """Gets the tag attributes from a line.
-
-    Parameters
-    ----------
-    tag_line: string
-        A tag line.
-
-    Returns
-    -------
-    list
-        A list containing the tag attributes.
-    """
-    attributes = re.compile(attributes_expression)
-    match = attributes.search(tag_line)
-    if match:
-        attributes_split = [x.strip() for x in re.sub('[{}]', '', match.group()).split(',')]
-        # Filter out attributes with spaces.
-        final_attributes = [a for a in attributes_split if " " not in a]
-        return final_attributes
-    else:
-        return ''
-
-
-def get_tag_description(tag_line):
-    """Gets the tag description from a line.
-
-    Parameters
-    ----------
-    tag_line: string
-        A tag line.
-
-    Returns
-    -------
-    string
-        The tag description.
-    """
-    description = re.compile(description_expression)
-    match = description.search(tag_line)
-    if match:
-        return re.sub(square_bracket_removal_expression, '', match.group()).strip()
-    else:
-        return ''
-
-
-def get_tag_level(tag_line):
-    """Gets the tag level from a line in a wiki file.
-
-    Parameters
-    ----------
-    tag_line: string
-        A tag line.
-
-    Returns
-    -------
-    int
-        Gets the tag level. The number of asterisks determine what level the tag is on.
-    """
-    level = re.compile(level_expression)
-    match = level.search(tag_line)
-    if match:
-        return match.group().count('*')
-    else:
-        return 1
-
-
-def add_tag_node(parent_node, tag_line):
-    """Adds a tag to its parent.
-
-    Parameters
-    ----------
-    parent_node: Element
-        The parent tag.
-    tag_line: string
-        A tag line.
-
-    Returns
-    -------
-    string
-        The tag line with the nowiki tag remove.
-    """
-    tag_node = None
-    tag_name = get_tag_name(tag_line)
-    if tag_name:
-        tag_description = get_tag_description(tag_line)
-        tag_attributes = get_tag_attributes(tag_line)
-        tag_node = SubElement(parent_node, tag_element)
-        name_node = SubElement(tag_node, tag_name_element)
-        name_node.text = tag_name
-        if tag_description:
-            description_node = SubElement(tag_node, tag_description_element)
-            description_node.text = tag_description
-        if tag_attributes:
-            add_tag_node_attributes(tag_node, tag_attributes)
-    return tag_node
-
-
-def add_unit_class_node(parent_node, unit_class, unit_class_units, unit_class_attributes, unit_class_unit_attributes):
-    """Adds a unit class to its parent.
-
-    Parameters
-    ----------
-    parent_node: Element
-        The parent of the unit class.
-    unit_class: Element
-        The unit class.
-    unit_class_units: list
-        A list of unit class units.
-    unit_class_attributes: list
-        A list of unit class attributes.
-    unit_class_unit_attributes: list
-        A list of attributes for a specific unit
-
-    Returns
-    -------
-    Element
-        The unit class element.
-    """
-    unit_class_node = SubElement(parent_node, unit_class_element)
-    name_node = SubElement(unit_class_node, unit_class_name_element)
-    name_node.text = unit_class
-    units_node = SubElement(unit_class_node, unit_class_units_element)
-    for unit, attributes in zip(unit_class_units, unit_class_unit_attributes):
-        unit_node = SubElement(units_node, unit_class_unit_element)
-        add_tag_node_attributes(unit_node, attributes)
-        unit_node.text = unit
-    if unit_class_attributes:
-        add_tag_node_attributes(unit_class_node, unit_class_attributes)
-    return unit_class_node
-
-
-def add_unit_modifier_node(parent_node, unit_modifier, unit_modifier_attributes, unit_modifier_description):
-    """Adds a unit modifier to its parent.
-
-    Parameters
-    ----------
-    parent_node: Element
-        The parent of the unit modifier.
-    unit_modifier: Element
-        The unit modifier.
-    unit_modifier_attributes: list
-        A list of unit modifier attributes.
-    unit_modifier_description: string
-        The unit modifier description.
-
-    Returns
-    -------
-    Element
-        The unit modifier element.
-    """
-    unit_modifier_node = SubElement(parent_node, unit_modifier_element)
-    name_node = SubElement(unit_modifier_node, unit_modifier_name_element)
-    name_node.text = unit_modifier
-    if unit_modifier_description:
-        description_node = SubElement(unit_modifier_node, unit_modifier_description_element)
-        description_node.text = unit_modifier_description
-    if unit_modifier_attributes:
-        add_tag_node_attributes(unit_modifier_node, unit_modifier_attributes)
-    return unit_modifier_node
-
-
-def add_tag_node_attributes(tag_node, tag_attributes):
-    """Adds the attributes to a tag.
-
-    Parameters
-    ----------
-    tag_node: Element
-        A tag element.
-    tag_attributes: list
-        A list containing the tag attributes.
-
-    Returns
-    -------
-
-    """
-    unit_classes = []
-    delimiter = ','
-    for attribute in tag_attributes:
-        if attribute.startswith(unit_class_attribute):
-            split_attribute = attribute.split('=')
-            unit_classes.append(split_attribute[1])
-        elif attribute.find('=') > -1:
-            split_attribute = attribute.split('=')
-            tag_node.set(split_attribute[0], split_attribute[1])
-        else:
-            tag_node.set(attribute, true_attribute)
-    if unit_classes:
-        tag_node.set(unit_class_attribute, delimiter.join(unit_classes))
diff --git a/hedtools/hed/schema/parsewiki.py b/hedtools/hed/schema/parsewiki.py
deleted file mode 100644
index d8546a6f4..000000000
--- a/hedtools/hed/schema/parsewiki.py
+++ /dev/null
@@ -1,249 +0,0 @@
-"""
-This module contains functions for parsing a wiki HED schema.
-"""
-
-from xml.etree.ElementTree import Element, SubElement
-from hed.schema import parsetag, constants
-from hed.util.exceptions import HedFileError, HedExceptions
-
-import os
-
-ATTRIBUTE_DEFINITION_STRING = '\'\'\'Attribute Definitions:'
-CHANGE_LOG_STRING = 'Changelog'
-SYNTAX_STRING = '\'\'\'Syntax'
-ROOT_TAG = '\'\'\''
-HED_NODE_STRING = "HED"
-START_STRING = '!# start hed'
-UNIT_CLASS_STRING = '\'\'\'Unit classes'
-UNIT_MODIFIER_STRING = '\'\'\'Unit modifiers'
-END_STRING = '!# end hed'
-hed_node = Element('HED')
-
-
-def add_tags(wiki_file):
-    """Adds the tags to the HED element.
-
-    Parameters
-    ----------
-    wiki_file: file object.
-        A file object that points to the HED wiki file.
-
-    Returns
-    -------
-
-    """
-    tag_levels = {}
-    line = wiki_file.readline()
-    while line:
-        line = parsetag.remove_nowiki_tag_from_line(line.strip())
-        if not line:
-            pass
-        elif line.startswith(UNIT_MODIFIER_STRING):
-            add_unit_modifiers(wiki_file)
-            break
-        elif line.startswith(UNIT_CLASS_STRING):
-            add_unit_classes(wiki_file)
-        elif line.startswith(ROOT_TAG):
-            root_tag = parsetag.add_tag_node(hed_node, line)
-            tag_levels[0] = root_tag
-        else:
-            level = parsetag.get_tag_level(line)
-            parent_tag = tag_levels[level - 1]
-            child_tag = parsetag.add_tag_node(parent_tag, line)
-            tag_levels[level] = child_tag
-        line = wiki_file.readline()
-
-
-def add_unit_classes(wiki_file):
-    """Adds the unit classes to the HED element.
-
-    Parameters
-    ----------
-    wiki_file: file object.
-        A file object that points to the HED wiki file.
-
-    Returns
-    -------
-
-    """
-    unit_class_node = SubElement(hed_node, 'unitClasses')
-    unit_class = ''
-    unit_class_units = []
-    unit_class_attributes = ''
-    unit_class_units_attributes = []
-    line = wiki_file.readline()
-    while line:
-        line = parsetag.remove_nowiki_tag_from_line(line.strip())
-        if not line:
-            break
-        else:
-            level = parsetag.get_tag_level(line)
-            if level == 1:
-                if unit_class_units:
-                    parsetag.add_unit_class_node(unit_class_node, unit_class, unit_class_units,
-                                                 unit_class_attributes, unit_class_units_attributes)
-                unit_class = parsetag.get_tag_name(line)
-                unit_class_attributes = parsetag.get_tag_attributes(line)
-                unit_class_units = []
-                unit_class_units_attributes = []
-            else:
-                unit_class_unit = parsetag.get_tag_name(line)
-                unit_class_unit_attributes = parsetag.get_tag_attributes(line)
-                unit_class_units.append(unit_class_unit)
-                unit_class_units_attributes.append(unit_class_unit_attributes)
-        line = wiki_file.readline()
-    parsetag.add_unit_class_node(unit_class_node, unit_class, unit_class_units,
-                                 unit_class_attributes, unit_class_units_attributes)
-
-
-def add_unit_modifiers(wiki_file):
-    """Adds the unit modifiers to the HED element.
-
-    Parameters
-    ----------
-    wiki_file: file object.
-        A file object that points to the HED wiki file.
-
-    Returns
-    -------
-
-    """
-    unit_modifier_node = SubElement(hed_node, 'unitModifiers')
-    line = wiki_file.readline()
-    while line:
-        line = parsetag.remove_nowiki_tag_from_line(line.strip())
-        if not line:
-            pass
-        elif line.startswith(END_STRING):
-            break
-        else:
-            unit_modifier = parsetag.get_tag_name(line)
-            unit_modifier_attributes = parsetag.get_tag_attributes(line)
-            unit_modifier_description = parsetag.get_tag_description(line)
-            parsetag.add_unit_modifier_node(unit_modifier_node, unit_modifier,
-                                            unit_modifier_attributes, unit_modifier_description)
-        line = wiki_file.readline()
-
-
-def hed_wiki_2_xml_tree(wiki_file_path):
-    """Converts a HED wiki file into a XML tree.
-
-    Parameters
-    ----------
-    wiki_file_path: str
-        The location of the HED wiki file.
-    Returns
-    -------
-    Element
-        A XML element containing the entire HED.
-    """
-    hed_node.clear()
-    try:
-        with open(wiki_file_path, 'r', encoding='utf-8', errors='replace') as wiki_file:
-            line = wiki_file.readline()
-            while line:
-                line = line.strip()
-                if not line:
-                    pass
-                if line.startswith(HED_NODE_STRING):
-                    hed_attributes = get_hed_attributes(line[len(HED_NODE_STRING):])
-                    for attrib_name, attrib_value in hed_attributes.items():
-                        hed_node.set(attrib_name, attrib_value)
-                elif line.startswith(START_STRING):
-                    add_tags(wiki_file)
-                    break
-                line = wiki_file.readline()
-    except FileNotFoundError as e:
-        raise HedFileError(HedExceptions.FILE_NOT_FOUND, e.strerror, wiki_file_path)
-    return hed_node
-
-
-def put_change_log_in_list(wiki_file):
-    """Puts the change log from a HED wiki file in a list.
-
-    Parameters
-    ----------
-    wiki_file: file object.
-        A file object that points to the HED wiki file.
-
-    Returns
-    -------
-    list
-        A list containing the change log of the HED wiki file.
-    """
-    change_log = []
-    index = 0
-    line = wiki_file.readline()
-    while line:
-        line = line.strip()
-        if not line:
-            pass
-        elif line.startswith(SYNTAX_STRING):
-            break
-        else:
-            level = parsetag.get_tag_level(line)
-            if level == 1:
-                change_log.append(parsetag.get_change_log_entry(line))
-                index += 1
-            else:
-                change_log[index - 1] += '\n' + parsetag.get_change_log_entry(line)
-        line = wiki_file.readline()
-    return change_log
-
-
-def get_hed_change_log(wiki_file_path):
-    """Gets the change log from a HED wiki file.
-
-    Parameters
-    ----------
-    wiki_file_path: string
-        The location of the HED wiki file.
-
-    Returns
-    -------
-    list
-        A list containing the change log of the HED wiki file.
-    """
-    change_log = []
-    if os.path.exists(wiki_file_path):
-        with open(wiki_file_path, 'r', encoding='utf-8', errors='replace') as wiki_file:
-            line = wiki_file.readline()
-            while line:
-                line = line.strip()
-                if not line:
-                    pass
-                if line.startswith(CHANGE_LOG_STRING):
-                    change_log = put_change_log_in_list(wiki_file)
-                    break
-                elif line.startswith(SYNTAX_STRING):
-                    break
-                line = wiki_file.readline()
-    return change_log
-
-
-def get_hed_attributes(version_line):
-    """Extracts all valid attributes like version from the HED line in .mediawiki format.
-
-    Parameters
-    ----------
-    version_line: string
-        The line in the wiki file that contains the version or other attributes.
-
-    Returns
-    -------
-    dict: The key is the name of the attribute, value being the value.  eg {'version':'v1.0.1'}
-    """
-    final_attributes = {}
-    attribute_pairs = version_line.split(',')
-    for pair in attribute_pairs:
-        if pair.count(':') != 1:
-            continue
-        key, value = pair.split(':')
-        key = key.strip()
-        value = value.strip()
-        if key not in constants.HED_VALID_ATTRIBUTES:
-            continue
-
-        final_attributes[key] = value
-
-    return final_attributes
diff --git a/hedtools/hed/schema/schema2wiki.py b/hedtools/hed/schema/schema2wiki.py
new file mode 100644
index 000000000..2816ce1a7
--- /dev/null
+++ b/hedtools/hed/schema/schema2wiki.py
@@ -0,0 +1,193 @@
+"""Allows output of HedSchema objects as .mediawiki format"""
+
+from hed.schema.hed_schema_constants import HedKey
+from hed.schema import hed_schema_constants as constants
+
+START_HED_STRING = "!# start schema"
+END_SCHEMA_STRING = "!# end schema"
+END_HED_STRING = "!# end hed"
+
+
+class HedSchema2Wiki:
+    def __init__(self):
+        self.current_tag_string = ""
+        self.current_tag_extra = ""
+        self.output = []
+
+    def process_schema(self, hed_schema):
+        """
+        Takes a HedSchema object and returns a list of strings representing it's .mediawiki version.
+
+        Parameters
+        ----------
+        hed_schema : HedSchema
+
+        Returns
+        -------
+        mediawiki_strings: [str]
+            A list of strings representing the .mediawiki version of this schema.
+        """
+        self.output = []
+
+        self._output_header(hed_schema)
+        self._output_tags(hed_schema)
+        self.current_tag_string = END_SCHEMA_STRING
+        self._flush_current_tag()
+        self._output_units(hed_schema)
+        self._output_unit_modifiers(hed_schema)
+        self._output_footer(hed_schema)
+
+        return self.output
+
+    def _output_header(self, hed_schema):
+        hed_attrib_string = self._get_attribs_string_from_schema(hed_schema)
+        self.current_tag_string = f"HED {hed_attrib_string}"
+        self._flush_current_tag()
+        self._add_blank_line()
+        self.current_tag_string += hed_schema.prologue
+        self._flush_current_tag()
+        self._add_blank_line()
+        self.current_tag_string = START_HED_STRING
+        self._flush_current_tag()
+
+    def _output_footer(self, hed_schema):
+        self._add_blank_line()
+        self.current_tag_string = END_HED_STRING
+        self._flush_current_tag()
+        self._add_blank_line()
+        self.current_tag_string += hed_schema.epilogue
+        self._flush_current_tag()
+
+    def _output_tags(self, hed_schema):
+        all_tags = hed_schema.get_all_tags()
+
+        for tag in all_tags:
+            if "/" not in tag:
+                self._add_blank_line()
+                self.current_tag_string += f"'''{tag}'''"
+            else:
+                count = tag.count("/")
+                short_tag = tag.split("/")[-1]
+                # takes value tags should appear after the nowiki tag.
+                if short_tag.endswith("#"):
+                    self.current_tag_string += f"{'*' * count}"
+                    self.current_tag_extra += short_tag + " "
+                else:
+                    self.current_tag_string += f"{'*' * count} {short_tag}"
+            self.current_tag_extra += self._format_props_and_desc(hed_schema, tag)
+            self._flush_current_tag()
+
+        self._add_blank_line()
+        self._add_blank_line()
+
+    def _output_units(self, hed_schema):
+        self.current_tag_string += "'''Unit classes'''"
+        self._flush_current_tag()
+        for unit_name, unit_types in hed_schema.dictionaries[HedKey.Units].items():
+            self.current_tag_string += f"* {unit_name}"
+            self.current_tag_extra += self._format_props_and_desc(hed_schema, unit_name, HedKey.Units,
+                                                                  keys=constants.UNIT_CLASS_ATTRIBUTES)
+            self._flush_current_tag()
+
+            for unit_type in unit_types:
+                self.current_tag_string += f"** {unit_type}"
+                self.current_tag_extra += self._format_props_and_desc(
+                    hed_schema, unit_type, HedKey.Units, keys=constants.UNIT_CLASS_ATTRIBUTES)
+                self._flush_current_tag()
+
+        self._add_blank_line()
+
+    def _output_unit_modifiers(self, hed_schema):
+        self.current_tag_string += "'''Unit modifiers'''"
+        self._flush_current_tag()
+        for modifier_name in hed_schema.dictionaries[HedKey.SIUnitModifier]:
+            self.current_tag_string += f"* {modifier_name}"
+            self.current_tag_extra += self._format_props_and_desc(
+                hed_schema, modifier_name, HedKey.SIUnitModifier, keys=constants.UNIT_MODIFIER_ATTRIBUTES)
+            self._flush_current_tag()
+
+    def _flush_current_tag(self):
+        if self.current_tag_string or self.current_tag_extra:
+            if self.current_tag_extra:
+                new_tag = f"{self.current_tag_string} <nowiki>{self.current_tag_extra}</nowiki>"
+            else:
+                new_tag = self.current_tag_string
+            self.output.append(new_tag)
+            self.current_tag_string = ""
+            self.current_tag_extra = ""
+
+    def _add_blank_line(self):
+        self.output.append("")
+
+    # Should this be a function?
+    def _format_props_and_desc(self, hed_schema, tag_name, tag_class=HedKey.AllTags, keys=None):
+        prop_string = ""
+        tag_props = hed_schema.get_all_tag_attributes(tag_name, keys)
+        if tag_props:
+            prop_string += self._format_tag_props(tag_props)
+        desc = hed_schema.get_tag_description(tag_name, tag_class)
+        if desc:
+            if tag_props:
+                prop_string += " "
+            prop_string += f"[{desc}]"
+
+        return prop_string
+
+    @staticmethod
+    def _format_tag_props(tag_props):
+        """
+            Takes a dictionary of tag attributes and returns a string with the .mediawiki representation
+
+        Parameters
+        ----------
+        tag_props : {str:str}
+            {attribute_name : attribute_value}
+        Returns
+        -------
+        str:
+            The formatted string that should be output to the file.
+        """
+        prop_string = ""
+        final_props = []
+        for prop, value in tag_props.items():
+            if value is None or value is False:
+                continue
+            if value is True or value == "true":
+                final_props.append(prop)
+            else:
+                if "," in value:
+                    split_values = value.split(",")
+                    for split_value in split_values:
+                        final_props.append(f"{prop}={split_value}")
+                else:
+                    final_props.append(f"{prop}={value}")
+
+        if final_props:
+            prop_string += "{"
+            final_prop_string = ", ".join(final_props)
+            prop_string += final_prop_string
+            prop_string += "}"
+
+        return prop_string
+
+    @staticmethod
+    def _get_attribs_string_from_schema(hed_schema):
+        """
+        Gets the schema attributes and converts it to a string.
+
+        Parameters
+        ----------
+        hed_schema : HedSchema
+            The schema to get attributes from
+
+        Returns
+        -------
+        str:
+            A string of the attributes that can be written to a .mediawiki formatted file
+        """
+        # Hardcode version to always be the first thing in attributes
+        attrib_values = [f"version:{hed_schema.schema_attributes['version']}"]
+        attrib_values.extend([f"{attr}:{value}" for attr, value in
+                              hed_schema.schema_attributes.items() if attr != "version"])
+        final_attrib_string = ", ".join(attrib_values)
+        return final_attrib_string
diff --git a/hedtools/hed/schema/schema2xml.py b/hedtools/hed/schema/schema2xml.py
new file mode 100644
index 000000000..45adbe605
--- /dev/null
+++ b/hedtools/hed/schema/schema2xml.py
@@ -0,0 +1,233 @@
+"""Allows output of HedSchema objects as .xml format"""
+
+from hed.schema.hed_schema_constants import HedKey
+from hed.schema import hed_schema_constants as constants
+from xml.etree.ElementTree import Element, SubElement
+
+TAG_ELEMENT = "node"
+TAG_NAME_ELEMENT = "name"
+TAG_DESCRIPTION_ELEMENT = "description"
+TRUE_ATTRIBUTE = "true"
+FALSE_ATTRIBUTE = "false"
+unit_class_element = 'unitClass'
+unit_class_name_element = 'name'
+unit_class_units_element = 'units'
+unit_class_unit_element = 'unit'
+unit_modifier_element = 'unitModifier'
+unit_modifier_name_element = 'name'
+unit_modifier_description_element = 'description'
+PROLOGUE_ELEMENT = "prologue"
+EPILOGUE_ELEMENT = "epilogue"
+
+
+class HedSchema2XML:
+    def __init__(self):
+        self.hed_node = None
+
+    def process_schema(self, hed_schema):
+        """
+        Takes a HedSchema object and returns a an XML tree version.
+
+        Parameters
+        ----------
+        hed_schema : HedSchema
+
+        Returns
+        -------
+        mediawiki_strings: [str]
+            A list of strings representing the .mediawiki version of this schema.
+        """
+        self.hed_node = Element('HED')
+
+        self._output_header(hed_schema)
+        self._output_tags(hed_schema)
+        self._output_units(hed_schema)
+        self._output_unit_modifiers(hed_schema)
+        self._output_footer(hed_schema)
+        return self.hed_node
+
+    # =========================================
+    # Top level output functions
+    # =========================================
+    def _output_header(self, hed_schema):
+        for attrib_name, attrib_value in hed_schema.schema_attributes.items():
+            self.hed_node.set(attrib_name, attrib_value)
+        if hed_schema.prologue:
+            prologue_node = SubElement(self.hed_node, PROLOGUE_ELEMENT)
+            prologue_node.text = hed_schema.prologue
+
+    def _output_tags(self, hed_schema):
+        all_tags = hed_schema.get_all_tags()
+        tag_levels = {}
+        for tag in all_tags:
+            if "/" not in tag:
+                root_tag = self._add_tag_node(hed_schema, self.hed_node, tag)
+                tag_levels[0] = root_tag
+            else:
+                level = tag.count("/")
+                short_tag = tag.split("/")[-1]
+                parent_tag = tag_levels[level - 1]
+                child_tag = self._add_tag_node(hed_schema, parent_tag, tag, short_tag)
+                tag_levels[level] = child_tag
+
+    def _output_units(self, hed_schema):
+        if not hed_schema.has_unit_classes:
+            return
+        unit_class_node = SubElement(self.hed_node, 'unitClasses')
+        for unit_class, unit_types in hed_schema.dictionaries[HedKey.Units].items():
+            unit_class_attributes = hed_schema.get_all_tag_attributes(unit_class, keys=constants.UNIT_CLASS_ATTRIBUTES)
+            unit_class_units = []
+            unit_class_units_attributes = []
+
+            for unit_class_unit in unit_types:
+                unit_class_unit_attributes = hed_schema.get_all_tag_attributes(unit_class_unit,
+                                                                               keys=constants.UNIT_CLASS_ATTRIBUTES)
+                unit_class_units.append(unit_class_unit)
+                unit_class_units_attributes.append(unit_class_unit_attributes)
+
+            self._add_unit_class_node(unit_class_node, unit_class, unit_class_units,
+                                      unit_class_attributes, unit_class_units_attributes)
+
+    def _output_unit_modifiers(self, hed_schema):
+        if not hed_schema.has_unit_modifiers:
+            return
+        unit_modifier_node = SubElement(self.hed_node, 'unitModifiers')
+        for modifier_name in hed_schema.dictionaries[HedKey.SIUnitModifier]:
+            unit_modifier_attributes = hed_schema.get_all_tag_attributes(modifier_name,
+                                                                         keys=constants.UNIT_MODIFIER_ATTRIBUTES)
+            unit_modifier_description = hed_schema.get_tag_description(modifier_name, HedKey.SIUnitModifier)
+            self._add_unit_modifier_node(unit_modifier_node, modifier_name,
+                                         unit_modifier_attributes, unit_modifier_description)
+
+    def _output_footer(self, hed_schema):
+        if hed_schema.epilogue:
+            prologue_node = SubElement(self.hed_node, EPILOGUE_ELEMENT)
+            prologue_node.text = hed_schema.epilogue
+
+    # =========================================
+    # Output helper functions to create nodes
+    # =========================================
+    @staticmethod
+    def _add_tag_node_attributes(tag_node, tag_attributes):
+        """Adds the attributes to a tag.
+
+        Parameters
+        ----------
+        tag_node: Element
+            A tag element.
+        tag_attributes: {str:str}
+            A dictionary of attributes to add to this node
+
+        Returns
+        -------
+
+        """
+        for attribute, value in tag_attributes.items():
+            if value is True:
+                tag_node.set(attribute, TRUE_ATTRIBUTE)
+            elif value is False:
+                continue
+            else:
+                tag_node.set(attribute, value)
+
+    @staticmethod
+    def _add_tag_node(hed_schema, parent_node, tag_name, short_tag_name=None):
+        """
+            Creates a tag node and adds it to the parent.
+
+        Parameters
+        ----------
+        hed_schema : HedSchema
+            HedSchema to pull tag info from
+        parent_node : Element
+            The parent tag node
+        tag_name : str
+            Long version of the tag name
+        short_tag_name : str
+            Short version of the tag name(even in HED 2G)
+
+        Returns
+        -------
+        Element
+            The added node
+        """
+        tag_node = None
+        if short_tag_name is None:
+            short_tag_name = tag_name
+        if tag_name:
+            tag_description = hed_schema.get_tag_description(tag_name)
+            tag_attributes = hed_schema.get_all_tag_attributes(tag_name)
+            tag_node = SubElement(parent_node, TAG_ELEMENT)
+            name_node = SubElement(tag_node, TAG_NAME_ELEMENT)
+            name_node.text = short_tag_name
+            if tag_description:
+                description_node = SubElement(tag_node, TAG_DESCRIPTION_ELEMENT)
+                description_node.text = tag_description
+            if tag_attributes:
+                HedSchema2XML._add_tag_node_attributes(tag_node, tag_attributes)
+        return tag_node
+
+    @staticmethod
+    def _add_unit_class_node(parent_node, unit_class, unit_class_units, unit_class_attributes,
+                             unit_class_unit_attributes):
+        """Adds a unit class to its parent.
+
+        Parameters
+        ----------
+        parent_node: Element
+            The parent of the unit class.
+        unit_class: Element
+            The unit class.
+        unit_class_units: list
+            A list of unit class units.
+        unit_class_attributes: list
+            A list of unit class attributes.
+        unit_class_unit_attributes: list
+            A list of attributes for a specific unit
+
+        Returns
+        -------
+        Element
+            The unit class element.
+        """
+        unit_class_node = SubElement(parent_node, unit_class_element)
+        name_node = SubElement(unit_class_node, unit_class_name_element)
+        name_node.text = unit_class
+        units_node = SubElement(unit_class_node, unit_class_units_element)
+        for unit, attributes in zip(unit_class_units, unit_class_unit_attributes):
+            unit_node = SubElement(units_node, unit_class_unit_element)
+            HedSchema2XML._add_tag_node_attributes(unit_node, attributes)
+            unit_node.text = unit
+        if unit_class_attributes:
+            HedSchema2XML._add_tag_node_attributes(unit_class_node, unit_class_attributes)
+        return unit_class_node
+
+    @staticmethod
+    def _add_unit_modifier_node(parent_node, unit_modifier, unit_modifier_attributes, unit_modifier_description):
+        """Adds a unit modifier to its parent.
+
+        Parameters
+        ----------
+        parent_node: Element
+            The parent of the unit modifier.
+        unit_modifier: Element
+            The unit modifier.
+        unit_modifier_attributes: list
+            A list of unit modifier attributes.
+        unit_modifier_description: string
+            The unit modifier description.
+
+        Returns
+        -------
+        Element
+            The unit modifier element.
+        """
+        unit_modifier_node = SubElement(parent_node, unit_modifier_element)
+        name_node = SubElement(unit_modifier_node, unit_modifier_name_element)
+        name_node.text = unit_modifier
+        if unit_modifier_description:
+            description_node = SubElement(unit_modifier_node, unit_modifier_description_element)
+            description_node.text = unit_modifier_description
+        if unit_modifier_attributes:
+            HedSchema2XML._add_tag_node_attributes(unit_modifier_node, unit_modifier_attributes)
+        return unit_modifier_node
diff --git a/hedtools/hed/schema/schema_validator.py b/hedtools/hed/schema/schema_compliance.py
similarity index 72%
rename from hedtools/hed/schema/schema_validator.py
rename to hedtools/hed/schema/schema_compliance.py
index 7639d9da9..e753fdea9 100644
--- a/hedtools/hed/schema/schema_validator.py
+++ b/hedtools/hed/schema/schema_compliance.py
@@ -1,4 +1,3 @@
-from hed.util.hed_schema import HedSchema
 from hed.util import error_reporter
 from hed.util.error_types import SchemaErrors, SchemaWarnings, ErrorContext
 
@@ -6,15 +5,15 @@
 ALLOWED_DESC_CHARS = "-_:;,./()+ ^"
 
 
-def validate_schema(hed_xml_file, also_check_for_warnings=True, display_filename=None,
-                    error_handler=None):
+def check_compliance(hed_schema, also_check_for_warnings=True, display_filename=None,
+                     error_handler=None):
     """
-        Does validation of schema and returns a list of errors and warnings.
+        Checks for hed3 compliance of a schema object.
 
     Parameters
     ----------
-    hed_xml_file : str
-        filepath to a HED XML file to validate
+    hed_schema : HedSchema
+        HedSchema object to check for hed3 compliance
     also_check_for_warnings : bool, default True
         If True, also checks for formatting issues like invalid characters, capitalization, etc.
     display_filename: str
@@ -30,25 +29,23 @@ def validate_schema(hed_xml_file, also_check_for_warnings=True, display_filename
     if error_handler is None:
         error_handler = error_reporter.ErrorHandler()
     issues_list = []
-    hed_dict = HedSchema(hed_xml_file)
 
     if not display_filename:
-        display_filename = hed_xml_file
+        display_filename = hed_schema.filename
     error_handler.push_error_context(ErrorContext.FILE_NAME, display_filename)
 
-    if hed_dict.has_duplicate_tags():
-        duplicate_dict = hed_dict.find_duplicate_tags()
+    if hed_schema.has_duplicate_tags():
+        duplicate_dict = hed_schema.find_duplicate_tags()
         for tag_name, long_org_tags in duplicate_dict.items():
             issues_list += error_handler.format_schema_error(SchemaErrors.DUPLICATE_TERMS, tag_name,
                                                              duplicate_tag_list=long_org_tags)
 
     if also_check_for_warnings:
-        hed_terms = hed_dict.get_all_terms()
+        hed_terms = hed_schema.get_all_tags(True)
         for hed_term in hed_terms:
             issues_list += validate_schema_term(hed_term, error_handler=error_handler)
 
-        tag_descs = hed_dict.get_all_descriptions()
-        for tag_name, desc in tag_descs.items():
+        for tag_name, desc in hed_schema.get_desc_dict().items():
             issues_list += validate_schema_description(tag_name, desc, error_handler=error_handler)
 
     error_handler.pop_error_context()
@@ -118,24 +115,3 @@ def validate_schema_description(tag_name, hed_description, error_handler):
                                                            hed_description, i, char)
     return issues_list
 
-
-def get_printable_issue_string(validation_issues, title=None, severity=None, skip_filename=True):
-    """Return a string with issues list flatted into single string, one per line
-
-    Parameters
-    ----------
-    validation_issues: []
-        Issues to print
-    title: str
-        Optional title that will always show up first if present(even if there are no validation issues)
-    severity: int
-        Return only warnings >= severity
-    skip_filename: bool
-        If true, don't add the filename context to the printable string.
-    Returns
-    -------
-    str
-        A str containing printable version of the issues or '[]'.
-
-    """
-    return error_reporter.ErrorHandler.get_printable_issue_string(validation_issues, title, severity, skip_filename)
diff --git a/hedtools/hed/schema/schema_validation_util.py b/hedtools/hed/schema/schema_validation_util.py
new file mode 100644
index 000000000..a9962e622
--- /dev/null
+++ b/hedtools/hed/schema/schema_validation_util.py
@@ -0,0 +1,43 @@
+from semantic_version import Version
+from hed.schema import hed_schema_constants as constants
+from hed.util.exceptions import HedExceptions, HedFileError
+
+
+def _validate_library_name(library_name):
+    if library_name.isalpha():
+        return True
+
+    for i, character in enumerate(library_name):
+        if not character.isalpha():
+            return f"Non alpha character '{character}' at position {i} in '{library_name}'"
+
+
+def _validate_version_string(version_string):
+    try:
+        Version(version_string)
+    except ValueError as e:
+        return str(e)
+
+    return True
+
+
+attribute_validators = {
+        "version": (_validate_version_string, HedExceptions.BAD_HED_SEMANTIC_VERSION),
+        "library": (_validate_library_name, HedExceptions.BAD_HED_LIBRARY_NAME)
+    }
+
+
+def validate_attributes(attrib_dict, filename):
+    for attribute_name, attribute_value in attrib_dict.items():
+        if attribute_name in attribute_validators:
+            validator, error_code = attribute_validators[attribute_name]
+            result = validator(attribute_value)
+            if result is not True:
+                raise HedFileError(error_code, result, filename)
+
+    if constants.VERSION_ATTRIBUTE not in attrib_dict:
+        raise HedFileError(HedExceptions.BAD_HED_SEMANTIC_VERSION, "No version attribute found in header",
+                           filename=filename)
+
+    # Placeholder for future warnings
+    return []
\ No newline at end of file
diff --git a/hedtools/hed/schema/wiki2schema.py b/hedtools/hed/schema/wiki2schema.py
new file mode 100644
index 000000000..6035f7037
--- /dev/null
+++ b/hedtools/hed/schema/wiki2schema.py
@@ -0,0 +1,534 @@
+"""
+This module is used to create a HedSchema object from a .mediawiki file.
+"""
+import re
+from hed.schema import hed_schema_constants as constants
+from hed.schema.hed_schema_constants import HedKey
+from hed.util.exceptions import HedFileError, HedExceptions
+from hed.schema import HedSchema
+from hed.schema import schema_validation_util
+
+ROOT_TAG = '\'\'\''
+HEADER_LINE_STRING = "HED"
+START_STRING = '!# start schema'
+END_SCHEMA_STRING = "!# end schema"
+UNIT_CLASS_STRING = '\'\'\'Unit classes'
+UNIT_MODIFIER_STRING = '\'\'\'Unit modifiers'
+ATTRIBUTE_DEFINITION_STRING = '\'\'\'Attribute Definitions:'
+END_STRING = '!# end hed'
+
+
+level_expression = r'\*+'
+attributes_expression = r'\{.*\}'
+description_expression = r'\[.*\]'
+extend_here_line = 'extend here'
+invalid_characters_to_strip = ["&#8203;"]
+tag_name_regexp = r'([<>=#\-a-zA-Z0-9$:()\^µ]+\s*)+'
+no_wiki_tag = '</?nowiki>'
+square_bracket_removal_expression = r'[\[\]]'
+
+
+# these must always be in order under the current spec.
+class HedSection:
+    HeaderLine = 2
+    Prologue = 3
+    Schema = 4
+    EndSchema = 5
+    UnitsClasses = 6
+    UnitModifiers = 7
+    Attributes = 8
+    EndHed = 9
+
+
+SectionStarts = {
+    HedSection.Schema: START_STRING,
+    HedSection.EndSchema: END_SCHEMA_STRING,
+    HedSection.UnitsClasses: UNIT_CLASS_STRING,
+    HedSection.UnitModifiers: UNIT_MODIFIER_STRING,
+    HedSection.Attributes: ATTRIBUTE_DEFINITION_STRING,
+    HedSection.EndHed: END_STRING
+}
+
+
+SectionNames = {
+    HedSection.HeaderLine: "Header",
+    HedSection.Prologue: "Prologue",
+    HedSection.Schema: "Schema",
+    HedSection.EndSchema: "EndSchema",
+    HedSection.UnitsClasses: "Unit Classes",
+    HedSection.UnitModifiers: "Unit Modifiers",
+    HedSection.Attributes: "Attributes",
+    HedSection.EndHed: "EndHed"
+}
+
+ErrorsBySection = {
+    HedSection.Schema: HedExceptions.SCHEMA_START_MISSING,
+    HedSection.EndSchema: HedExceptions.SCHEMA_END_INVALID,
+    HedSection.EndHed: HedExceptions.HED_END_INVALID
+}
+required_sections = [HedSection.Schema, HedSection.EndSchema, HedSection.EndHed]
+
+
+class HedSchemaWikiParser:
+    def __init__(self, wiki_file_path):
+        self.filename = wiki_file_path
+        # Required properties
+        self.schema_attributes = {}
+        self.dictionaries = HedSchema.create_empty_dictionaries()
+        self.prologue = ""
+        self.epilogue = ""
+
+        self.issues = []
+
+        # Variables used while parsing.
+        self._found_sections = {}
+        self._current_section = HedSection.HeaderLine
+
+        try:
+            with open(wiki_file_path, 'r', encoding='utf-8', errors='replace') as wiki_file:
+                self._read_file(wiki_file)
+        except FileNotFoundError as e:
+            raise HedFileError(HedExceptions.FILE_NOT_FOUND, e.strerror, wiki_file_path)
+
+    @staticmethod
+    def load_wiki(wiki_file_path):
+        parser = HedSchemaWikiParser(wiki_file_path)
+
+        hed_schema = HedSchema()
+
+        hed_schema.issues = parser.issues
+        hed_schema.filename = parser.filename
+        hed_schema.prologue = parser.prologue
+        hed_schema.epilogue = parser.epilogue
+        hed_schema.set_dictionaries(parser.dictionaries)
+        hed_schema.set_attributes(parser.schema_attributes)
+
+        return hed_schema
+
+    def _get_line_iter(self, wiki_file):
+        """ This function iterates over the file line by line, keeping track of which file section it is currently in.
+        Parameters
+        ----------
+        wiki_file : file object
+            An opened .mediawiki file
+        """
+        line = wiki_file.readline()
+        while line:
+            for key, section_string in SectionStarts.items():
+                if line.startswith(section_string):
+                    if key in self._found_sections:
+                        raise HedFileError(HedExceptions.INVALID_SECTION_SEPARATOR,
+                                           f"Found section {SectionNames[key]} twice", filename=self.filename)
+                    self._found_sections[key] = True
+
+                    if self._current_section < key:
+                        self._current_section = key
+                        # this line is already handled and we are in a new section
+                        yield False
+                        line = wiki_file.readline()
+                        continue
+                    else:
+                        error_code = HedExceptions.INVALID_SECTION_SEPARATOR
+                        if key in ErrorsBySection:
+                            error_code = ErrorsBySection[key]
+
+                        raise HedFileError(error_code,
+                                           f"Found section {SectionNames[key]} out of order in file",
+                                           filename=self.filename)
+
+            if line.startswith("!#"):
+                raise HedFileError(HedExceptions.INVALID_SECTION_SEPARATOR,
+                                   f"Invalid section separator '{line.strip()}'", filename=self.filename)
+
+            if self._current_section == HedSection.Prologue or self._current_section == HedSection.EndHed:
+                if line.strip():
+                    # we want to preserve all formatting in the prologue and epilogue.
+                    if line.endswith("\n"):
+                        line = line[:-1]
+                    yield line
+            else:
+                line = self._remove_nowiki_tag_from_line(line.strip())
+                if line:
+                    yield line
+
+            line = wiki_file.readline()
+        self._current_section = None
+
+    def _read_file(self, wiki_file):
+        """
+        Calls the parsers for each section as this goes through the file.
+
+        Parameters
+        ----------
+        wiki_file : file object
+            An opened .mediawiki file
+        """
+        self._current_section = HedSection.HeaderLine
+
+        parsers = {
+            HedSection.HeaderLine: self._read_header_line,
+            HedSection.Prologue: self._read_prologue,
+            HedSection.Schema: self._read_schema,
+            HedSection.EndSchema: self._skip_read_section,
+            HedSection.UnitsClasses: self._read_unit_classes,
+            HedSection.UnitModifiers: self._read_unit_modifiers,
+            HedSection.Attributes: self._skip_read_section,
+            HedSection.EndHed: self._read_epilogue,
+        }
+
+        file_iter = self._get_line_iter(wiki_file)
+
+        while self._current_section is not None:
+            # The iterator is responsible for updating the current_section variable.
+            # it will be set to None when at the end of the file.
+            parsers[self._current_section](file_iter)
+
+        # Validate we didn't miss any required sections.
+        for section in required_sections:
+            if section not in self._found_sections:
+                error_code = HedExceptions.INVALID_SECTION_SEPARATOR
+                if section in ErrorsBySection:
+                    error_code = ErrorsBySection[section]
+                raise HedFileError(error_code, f"Required section separator '{SectionNames[section]}' not found in file",
+                                   filename=self.filename)
+
+    def _read_header_line(self, file_iter):
+        """Adds the header line
+
+        Parameters
+        ----------
+        file_iter: iter
+            An iterator from self._get_line_iter.
+        """
+        first_line = next(file_iter)
+
+        # First line MUST be the HED line with full proper formatting.
+        if first_line.startswith(HEADER_LINE_STRING):
+            hed_attributes = self._get_schema_attributes(first_line[len(HEADER_LINE_STRING):])
+            self.issues = schema_validation_util.validate_attributes(hed_attributes, filename=self.filename)
+            self.schema_attributes = hed_attributes
+        else:
+            raise HedFileError(HedExceptions.SCHEMA_HEADER_MISSING,
+                               f"First line of file should be HED, instead found: {first_line}", filename=self.filename)
+
+        self._current_section = HedSection.Prologue
+
+    def _read_prologue(self, file_iter):
+        """Adds the prologue
+
+        Parameters
+        ----------
+        file_iter: iter
+            An iterator from self._get_line_iter.
+        """
+        for line in file_iter:
+            if line is False:
+                return
+
+            if self.prologue:
+                self.prologue += "\n"
+            self.prologue += line
+
+    def _read_epilogue(self, file_iter):
+        """Adds the epilogue
+
+        Parameters
+        ----------
+        file_iter: iter
+            An iterator from self._get_line_iter.
+        """
+        for line in file_iter:
+            if line is False:
+                return
+
+            if self.epilogue:
+                self.epilogue += "\n"
+            self.epilogue += line
+
+    def _read_schema(self, file_iter):
+        """Adds the main schema section
+
+        Parameters
+        ----------
+        file_iter: iter
+            An iterator from self._get_line_iter.
+        """
+        parent_tags = []
+        for line in file_iter:
+            if line is False:
+                return
+
+            if line.startswith(ROOT_TAG):
+                parent_tags = []
+                new_tag = self._add_tag_line(parent_tags, line)
+                parent_tags.append(new_tag)
+            else:
+                level = self._get_tag_level(line)
+                if level < len(parent_tags):
+                    parent_tags = parent_tags[:level]
+                new_tag = self._add_tag_line(parent_tags, line)
+                parent_tags.append(new_tag)
+
+    def _skip_read_section(self, file_iter):
+        """Reads from the file until it reaches a new section, discarding all lines.
+
+        Parameters
+        ----------
+        file_iter: iter
+            An iterator from self._get_line_iter.
+        """
+        for line in file_iter:
+            if line is False:
+                return
+
+    def _read_unit_classes(self, file_iter):
+        """Adds the unit classes section
+
+        Parameters
+        ----------
+        file_iter: iter
+            An iterator from self._get_line_iter.
+        """
+        self.dictionaries[HedKey.DefaultUnits] = {}
+        self.dictionaries[HedKey.Units] = {}
+        for unit_class_key in constants.UNIT_CLASS_DICTIONARY_KEYS:
+            self.dictionaries[unit_class_key] = {}
+        current_unit_class = ""
+
+        for line in file_iter:
+            if line is False:
+                return
+
+            level = self._get_tag_level(line)
+            # This is a unit class
+            if level == 1:
+                unit_class = self._get_tag_name(line)
+                unit_class_attributes = self._get_tag_attributes(line)
+                current_unit_class = unit_class
+                unit_class_desc = self._get_tag_description(line)
+                if unit_class_desc:
+                    self.dictionaries[HedKey.Descriptions][HedKey.Units + unit_class] = unit_class_desc
+                for tag_attribute in unit_class_attributes:
+                    if tag_attribute in constants.STRING_ATTRIBUTE_DICTIONARY_KEYS:
+                        attribute_value = unit_class_attributes[tag_attribute]
+                    else:
+                        attribute_value = unit_class
+                    self.dictionaries[tag_attribute][unit_class] = attribute_value
+            else:
+                unit_class_unit = self._get_tag_name(line)
+                unit_class_unit_attributes = self._get_tag_attributes(line)
+                unit_class_desc = self._get_tag_description(line)
+                if unit_class_desc:
+                    self.dictionaries[HedKey.Descriptions][HedKey.Units + unit_class_unit] = unit_class_desc
+
+                if current_unit_class not in self.dictionaries[HedKey.Units]:
+                    self.dictionaries[HedKey.Units][current_unit_class] = []
+                self.dictionaries[HedKey.Units][current_unit_class].append(unit_class_unit)
+
+                for unit_class_key in constants.UNIT_CLASS_DICTIONARY_KEYS:
+                    self.dictionaries[unit_class_key][unit_class_unit] = unit_class_unit_attributes.get(
+                        unit_class_key)
+
+    def _read_unit_modifiers(self, file_iter):
+        """Adds the unit modifiers section
+
+        Parameters
+        ----------
+        file_iter: iter
+            An iterator from self._get_line_iter.
+        """
+        self.dictionaries[HedKey.SIUnitModifier] = {}
+        self.dictionaries[HedKey.SIUnitSymbolModifier] = {}
+        for line in file_iter:
+            if line is False:
+                return
+
+            unit_modifier = self._get_tag_name(line)
+            unit_modifier_attributes = self._get_tag_attributes(line)
+            unit_modifier_desc = self._get_tag_description(line)
+
+            if unit_modifier_desc:
+                self.dictionaries[HedKey.Descriptions][HedKey.SIUnitModifier + unit_modifier] = unit_modifier_desc
+
+            for unit_modifier_key in constants.UNIT_MODIFIER_DICTIONARY_KEYS:
+                self.dictionaries[unit_modifier_key][unit_modifier] = unit_modifier_attributes.get(unit_modifier_key)
+
+    def _get_schema_attributes(self, version_line):
+        """Extracts all valid attributes like version from the HED line in .mediawiki format.
+
+        Parameters
+        ----------
+        version_line: string
+            The line in the wiki file that contains the version or other attributes.
+
+        Returns
+        -------
+        {}: The key is the name of the attribute, value being the value.  eg {'version':'v1.0.1'}
+        """
+        final_attributes = {}
+        attribute_pairs = version_line.split(',')
+        for pair in attribute_pairs:
+            if pair.count(':') != 1:
+                raise HedFileError(HedExceptions.SCHEMA_HEADER_INVALID,
+                                   f"Found poorly matched key:value pair in header: {pair}", filename=self.filename)
+            key, value = pair.split(':')
+            key = key.strip()
+            value = value.strip()
+            final_attributes[key] = value
+
+        return final_attributes
+
+    @staticmethod
+    def _get_tag_level(tag_line):
+        """Gets the tag level from a line in a wiki file.
+
+        Parameters
+        ----------
+        tag_line: string
+            A tag line.
+
+        Returns
+        -------
+        int
+            Gets the tag level. The number of asterisks determine what level the tag is on.
+        """
+        level = re.compile(level_expression)
+        match = level.search(tag_line)
+        if match:
+            return match.group().count('*')
+        else:
+            return 1
+
+    @staticmethod
+    def _remove_nowiki_tag_from_line(tag_line):
+        """Removes the nowiki tag from the  line.
+
+        Parameters
+        ----------
+        tag_line: string
+            A tag line.
+
+        Returns
+        -------
+        string
+            The line with the nowiki tag removed.
+        """
+        tag_line = re.sub(no_wiki_tag, '', tag_line)
+        return tag_line
+
+    @staticmethod
+    def _get_tag_name(tag_line):
+        """Gets the tag name from the tag line.
+
+        Parameters
+        ----------
+        tag_line: string
+            A tag line.
+
+        Returns
+        -------
+        string
+            The tag name.
+        """
+        if tag_line.find(extend_here_line) != -1:
+            return ''
+        name = re.compile(tag_name_regexp)
+        for invalid_chars in invalid_characters_to_strip:
+            tag_line = tag_line.replace(invalid_chars, "")
+        match = name.search(tag_line)
+        if match:
+            return match.group().strip()
+        else:
+            return ''
+
+    @staticmethod
+    def _get_tag_attributes(tag_line):
+        """Gets the tag attributes from a line.
+
+        Parameters
+        ----------
+        tag_line: string
+            A tag line.
+
+        Returns
+        -------
+        {}
+            Dict containing the attributes
+        """
+        attributes = re.compile(attributes_expression)
+        match = attributes.search(tag_line)
+        if match:
+            attributes_split = [x.strip() for x in re.sub('[{}]', '', match.group()).split(',')]
+            # Filter out attributes with spaces.
+            attributes_split = [a for a in attributes_split if " " not in a]
+
+            final_attributes = {}
+            for attribute in attributes_split:
+                split_attribute = attribute.split("=")
+                if len(split_attribute) == 1:
+                    final_attributes[split_attribute[0]] = "true"
+                else:
+                    if split_attribute[0] in final_attributes:
+                        final_attributes[split_attribute[0]] += "," + split_attribute[1]
+                    else:
+                        final_attributes[split_attribute[0]] = split_attribute[1]
+            return final_attributes
+        else:
+            return {}
+
+    @staticmethod
+    def _get_tag_description(tag_line):
+        """Gets the tag description from a line.
+
+        Parameters
+        ----------
+        tag_line: string
+            A tag line.
+
+        Returns
+        -------
+        string
+            The tag description.
+        """
+        description = re.compile(description_expression)
+        match = description.search(tag_line)
+        if match:
+            return re.sub(square_bracket_removal_expression, '', match.group()).strip()
+        else:
+            return ''
+
+    def _add_tag_line(self, parent_tags, tag_line):
+        """Add a tag to the dictionaries, including attributes and description.
+
+        Parameters
+        ----------
+        parent_tags: [str]
+            A list of parent tags in order.
+        tag_line: string
+            A tag line.
+
+        Returns
+        -------
+        tag_name: str
+            The name of the added tag
+        """
+        tag_name = self._get_tag_name(tag_line)
+        if tag_name:
+            tag_description = self._get_tag_description(tag_line)
+            tag_attributes = self._get_tag_attributes(tag_line)
+            if parent_tags:
+                long_tag_name = "/".join(parent_tags) + "/" + tag_name
+            else:
+                long_tag_name = tag_name
+            self.dictionaries[HedKey.AllTags][long_tag_name.lower()] = long_tag_name
+            if tag_description:
+                self.dictionaries[HedKey.Descriptions][long_tag_name] = tag_description
+            for tag_attribute in tag_attributes:
+                if tag_attribute in constants.STRING_ATTRIBUTE_DICTIONARY_KEYS:
+                    attribute_value = tag_attributes[tag_attribute]
+                else:
+                    attribute_value = long_tag_name
+                if tag_attribute in constants.TAG_ATTRIBUTE_KEYS:
+                    self.dictionaries[tag_attribute][long_tag_name.lower()] = attribute_value
+
+        return tag_name
diff --git a/hedtools/hed/schema/wiki2xml.py b/hedtools/hed/schema/wiki2xml.py
deleted file mode 100644
index eae8cad12..000000000
--- a/hedtools/hed/schema/wiki2xml.py
+++ /dev/null
@@ -1,59 +0,0 @@
-"""
-This module contains functions that convert a wiki HED schema into a XML HED schema.
-"""
-from hed.util import file_util
-from hed.schema import parsewiki
-from hed.schema.schema_validator import validate_schema
-
-
-def convert_hed_wiki_2_xml(hed_wiki_url, local_wiki_file=None, check_for_issues=True,
-                           display_filename=None):
-    """Converts the HED wiki into a XML file.
-
-    Parameters
-    ----------
-    hed_wiki_url: string or None
-        url pointing to the .mediawiki file to use
-    local_wiki_file: str or None
-        local wiki file to use(overrides hed_wiki_url)
-    check_for_issues : bool
-        After conversion checks for warnings like capitalization or invalid characters.
-    display_filename: str
-        If present, will use this as the filename for context, rather than using the actual filename
-        Useful for temp filenames.
-    Returns
-    -------
-    xml_filename: str
-        Location of output XML file, None on complete failure
-    issues_list: [{}]
-        returns a list of error/warning dictionaries
-    """
-    if local_wiki_file is None:
-        local_wiki_file = file_util.url_to_file(hed_wiki_url)
-
-    hed_xml_file_location = _create_hed_xml_file(local_wiki_file)
-
-    issue_list = []
-    if check_for_issues:
-        warnings = validate_schema(hed_xml_file_location, display_filename=display_filename)
-        issue_list += warnings
-
-    return hed_xml_file_location, issue_list
-
-
-def _create_hed_xml_file(hed_wiki_file_path):
-    """Creates a HED XML file from a HED wiki.
-
-    Parameters
-    ----------
-    hed_wiki_file_path: string
-        The path to the local HED wiki file.
-
-    Returns
-    -------
-    filename: str
-        Path to hed XML file
-    """
-    hed_xml_tree = parsewiki.hed_wiki_2_xml_tree(hed_wiki_file_path)
-    hed_xml_file_location = file_util.write_xml_tree_2_xml_file(hed_xml_tree)
-    return hed_xml_file_location
diff --git a/hedtools/hed/schema/xml2schema.py b/hedtools/hed/schema/xml2schema.py
new file mode 100644
index 000000000..5ce2e9f1c
--- /dev/null
+++ b/hedtools/hed/schema/xml2schema.py
@@ -0,0 +1,467 @@
+"""
+This module is used to create a HedSchema object from an XML file or tree.
+"""
+
+from defusedxml.ElementTree import parse
+import xml
+from hed.util.exceptions import HedFileError, HedExceptions
+from hed.schema import hed_schema_constants as constants
+from hed.schema.hed_schema_constants import HedKey
+from hed.schema import HedSchema
+
+
+class HedSchemaXMLParser:
+    # These should mostly match the HedKey values
+    # These are repeated here for clarification primarily
+    DEFAULT_UNITS_FOR_TYPE_ATTRIBUTE = HedKey.DefaultUnits
+    DEFAULT_UNIT_FOR_OLD_UNIT_CLASS_ATTRIBUTE = HedKey.Default
+    UNIT_CLASS_ELEMENT = HedKey.UnitClass
+    UNIT_CLASS_UNIT_ELEMENT = 'unit'
+    UNIT_CLASS_UNITS_ELEMENT = HedKey.Units
+    UNIT_MODIFIER_ELEMENT = 'unitModifier'
+    PROLOGUE_ELEMENT = "prologue"
+    EPILOGUE_ELEMENT = "epilogue"
+
+    def __init__(self, hed_xml_file_path):
+        self._root_element = self._parse_hed_xml_file(hed_xml_file_path)
+        # Used to find parent elements of XML nodes during file parsing
+        self._parent_map = {c: p for p in self._root_element.iter() for c in p}
+
+        # Required properties
+        self.schema_attributes = self._get_schema_attributes()
+        self.dictionaries = HedSchema.create_empty_dictionaries()
+        self.prologue = self._get_prologue()
+        self.epilogue = self._get_epilogue()
+        self._populate_dictionaries()
+
+    @staticmethod
+    def load_xml(xml_file_path):
+        parser = HedSchemaXMLParser(xml_file_path)
+
+        hed_schema = HedSchema()
+
+        hed_schema.filename = xml_file_path
+        hed_schema.prologue = parser.prologue
+        hed_schema.epilogue = parser.epilogue
+        hed_schema.set_dictionaries(parser.dictionaries)
+        hed_schema.set_attributes(parser.schema_attributes)
+
+        return hed_schema
+
+    @staticmethod
+    def _parse_hed_xml_file(hed_xml_file_path):
+        """Parses a XML file and returns the root element.
+
+        Parameters
+        ----------
+        hed_xml_file_path: str
+            The path to a HED XML file.
+
+        Returns
+        -------
+        RestrictedElement
+            The root element of the HED XML file.
+
+        """
+        try:
+            hed_xml_tree = parse(hed_xml_file_path)
+        except xml.etree.ElementTree.ParseError as e:
+            raise HedFileError(HedExceptions.CANNOT_PARSE_XML, e.msg, hed_xml_file_path)
+        except FileNotFoundError as e:
+            raise HedFileError(HedExceptions.FILE_NOT_FOUND, e.strerror, hed_xml_file_path)
+        except TypeError as e:
+            raise HedFileError(HedExceptions.FILE_NOT_FOUND, str(e), hed_xml_file_path)
+
+        return hed_xml_tree.getroot()
+
+    def _populate_dictionaries(self):
+        """Populates a dictionary of dictionaries that contains all of the tags, tag attributes, unit class units,
+           and unit class attributes.
+
+        Parameters
+        ----------
+
+        Returns
+        -------
+        {}
+            A dictionary of dictionaries that contains all of the tags, tag attributes, unit class units, and unit class
+            attributes.
+
+        """
+        self._populate_tag_dictionaries()
+        self._populate_unit_class_dictionaries()
+        self._populate_unit_modifier_dictionaries()
+
+    def _populate_tag_dictionaries(self):
+        """Populates a dictionary of dictionaries associated with tags and their attributes.
+
+        Parameters
+        ----------
+
+        Returns
+        -------
+        {}
+            A dictionary of dictionaries that has been populated with dictionaries associated with tag attributes.
+
+        """
+        for dict_key in constants.TAG_ATTRIBUTE_KEYS:
+            tags, tag_elements = self._get_tags_by_attribute(dict_key)
+            if dict_key in constants.STRING_ATTRIBUTE_DICTIONARY_KEYS:
+                tag_dictionary = self._create_tag_to_attribute_dictionary(tags, tag_elements, dict_key)
+            else:
+                tag_dictionary = self._string_list_to_lowercase_dictionary(tags)
+            self.dictionaries[dict_key] = tag_dictionary
+
+        # Finally handle the special cases of all tags, and descriptions
+        tags, tag_elements = self._get_all_tags()
+        tag_dictionary = self._string_list_to_lowercase_dictionary(tags)
+        self.dictionaries[HedKey.Descriptions] = self._get_element_desc(tags, tag_elements)
+        self.dictionaries[HedKey.AllTags] = tag_dictionary
+
+    def _populate_unit_class_dictionaries(self):
+        """Populates a dictionary of dictionaries associated with all of the unit classes, unit class units, and unit
+           class default units.
+
+        Parameters
+        ----------
+
+        Returns
+        -------
+        {}
+            A dictionary of dictionaries associated with all of the unit classes, unit class units, and unit class
+            default units.
+
+        """
+        unit_class_elements = self._get_elements_by_name(self.UNIT_CLASS_ELEMENT)
+        if len(unit_class_elements) == 0:
+            self.has_unit_classes = False
+            return
+        self.has_unit_classes = True
+        self._populate_unit_class_default_unit_dictionary(unit_class_elements)
+        self._populate_unit_class_units_dictionary(unit_class_elements)
+
+    def _populate_unit_modifier_dictionaries(self):
+        """
+        Gets all unit modifier definitions from the
+
+        Returns
+        -------
+
+        """
+        unit_modifier_elements = self._get_elements_by_name(self.UNIT_MODIFIER_ELEMENT)
+        if len(unit_modifier_elements) == 0:
+            self.has_unit_modifiers = False
+            return
+        self.has_unit_modifiers = True
+        for unit_modifier_key in constants.UNIT_MODIFIER_DICTIONARY_KEYS:
+            self.dictionaries[unit_modifier_key] = {}
+        for unit_modifier_element in unit_modifier_elements:
+            unit_modifier_name = self._get_element_tag_value(unit_modifier_element)
+            unit_modifier_desc = self._get_element_tag_value(unit_modifier_element, "description")
+            if unit_modifier_desc:
+                self.dictionaries[HedKey.Descriptions][HedKey.SIUnitModifier + unit_modifier_name] = unit_modifier_desc
+            for unit_modifier_key in constants.UNIT_MODIFIER_DICTIONARY_KEYS:
+                self.dictionaries[unit_modifier_key][unit_modifier_name] = unit_modifier_element.get(unit_modifier_key)
+
+    def _populate_unit_class_units_dictionary(self, unit_class_elements):
+        """Populates a dictionary that contains unit class units.
+
+        Parameters
+        ----------
+        unit_class_elements: list
+            A list of unit class elements.
+
+        Returns
+        -------
+        {}
+            A dictionary that contains all the unit class units.
+
+        """
+        self.dictionaries[HedKey.Units] = {}
+        for unit_class_key in constants.UNIT_CLASS_DICTIONARY_KEYS:
+            self.dictionaries[unit_class_key] = {}
+        for unit_class_element in unit_class_elements:
+            element_name = self._get_element_tag_value(unit_class_element)
+            element_desc = self._get_element_tag_value(unit_class_element, "description")
+            if element_desc:
+                self.dictionaries[HedKey.Descriptions][HedKey.Units + element_name] = element_desc
+            element_units = self._get_elements_by_name(self.UNIT_CLASS_UNIT_ELEMENT, unit_class_element)
+            if not element_units:
+                element_units = self._get_element_tag_value(unit_class_element, self.UNIT_CLASS_UNITS_ELEMENT)
+                units = element_units.split(',')
+                units_list = list(map(lambda unit: unit.lower(), units))
+                self.dictionaries[HedKey.Units][element_name] = units_list
+                continue
+            element_unit_names = list(map(lambda element: element.text, element_units))
+            self.dictionaries[HedKey.Units][element_name] = element_unit_names
+            for element_unit in element_units:
+                unit_name = element_unit.text
+                for unit_class_key in constants.UNIT_CLASS_DICTIONARY_KEYS:
+                    self.dictionaries[unit_class_key][unit_name] = element_unit.get(unit_class_key)
+
+    def _populate_unit_class_default_unit_dictionary(self, unit_class_elements):
+        """Populates a dictionary that contains unit class default units.
+
+        Parameters
+        ----------
+        unit_class_elements: list
+            A list of unit class elements.
+
+        Returns
+        -------
+        {}
+            A dictionary that contains all the unit class default units.
+
+        """
+        self.dictionaries[HedKey.DefaultUnits] = {}
+        for unit_class_element in unit_class_elements:
+            unit_class_element_name = self._get_element_tag_value(unit_class_element)
+            # this is handled in _populate_unit_class_units_dictionary for now.
+            # unit_class_desc = self._get_element_tag_value(unit_class_element, "description")
+            default_unit = unit_class_element.get(self.DEFAULT_UNITS_FOR_TYPE_ATTRIBUTE)
+            if default_unit is None:
+                self.dictionaries[HedKey.DefaultUnits][unit_class_element_name] = \
+                    unit_class_element.attrib[self.DEFAULT_UNIT_FOR_OLD_UNIT_CLASS_ATTRIBUTE]
+            else:
+                self.dictionaries[HedKey.DefaultUnits][unit_class_element_name] = default_unit
+
+    @staticmethod
+    def _create_tag_to_attribute_dictionary(tag_list, tag_element_list, attribute_name):
+        """Populates the dictionaries associated with default unit tags in the attribute dictionary.
+
+        Parameters
+        ----------
+        tag_list: []
+            A list containing tags that have a specific attribute.
+        tag_element_list: []
+            A list containing tag elements that have a specific attribute.
+        attribute_name: str
+            The name of the attribute associated with the tags and tag elements.
+
+        Returns
+        -------
+        {}
+            The attribute dictionary that has been populated with dictionaries associated with tags.
+
+        """
+        dictionary = {}
+        for index, tag in enumerate(tag_list):
+            dictionary[tag.lower()] = tag_element_list[index].attrib[attribute_name]
+        return dictionary
+
+    @staticmethod
+    def _string_list_to_lowercase_dictionary(string_list):
+        """Converts a string list into a dictionary. The keys in the dictionary will be the lowercase values of the
+           strings in the list.
+
+        Parameters
+        ----------
+        string_list: list
+            A list containing string elements.
+
+        Returns
+        -------
+        {}
+            A dictionary containing the strings in the list.
+
+        """
+        lowercase_dictionary = {}
+        for string_element in string_list:
+            lowercase_dictionary[string_element.lower()] = string_element
+        return lowercase_dictionary
+
+    def _get_ancestor_tag_names(self, tag_element):
+        """Gets all the ancestor tag names of a tag element.
+
+        Parameters
+        ----------
+        tag_element: Element
+            A tag element in the HED XML file.
+
+        Returns
+        -------
+        []
+            A list containing all of the ancestor tag names of a given tag.
+
+        """
+        ancestor_tags = []
+        parent_tag_name = self._get_parent_tag_name(tag_element)
+        parent_element = self._parent_map[tag_element]
+        while parent_tag_name:
+            ancestor_tags.append(parent_tag_name)
+            parent_tag_name = self._get_parent_tag_name(parent_element)
+            if parent_tag_name:
+                parent_element = self._parent_map[parent_element]
+        return ancestor_tags
+
+    @staticmethod
+    def _get_element_tag_value(element, tag_name='name'):
+        """Gets the value of the element's tag.
+
+        Parameters
+        ----------
+        element: Element
+            A element in the HED XML file.
+        tag_name: str
+            The name of the XML element's tag. The default is 'name'.
+
+        Returns
+        -------
+        str
+            The value of the element's tag. If the element doesn't have the tag then it will return an empty string.
+
+        """
+        element = element.find(tag_name)
+        if element is not None:
+            return element.text
+        return ""
+
+    def _get_parent_tag_name(self, tag_element):
+        """Gets the name of the tag parent element.
+
+        Parameters
+        ----------
+        tag_element: Element
+            A tag element in the HED XML file.
+
+        Returns
+        -------
+        str
+            The name of the tag element's parent. If there is no parent tag then an empty string is returned.
+
+        """
+        parent_tag_element = self._parent_map[tag_element]
+        if parent_tag_element is not None:
+            return parent_tag_element.findtext('name')
+        else:
+            return ''
+
+    def _get_tag_path_from_tag_element(self, tag_element):
+        """Gets the tag path from a given tag element.
+
+        Parameters
+        ----------
+        tag_element: Element
+            A tag element in the HED XML file.
+
+        Returns
+        -------
+        str
+            A tag path which is typically referred to as a tag. The tag and it's ancestor tags will be separated by /'s.
+
+        """
+        ancestor_tag_names = self._get_ancestor_tag_names(tag_element)
+        ancestor_tag_names.insert(0, self._get_element_tag_value(tag_element))
+        ancestor_tag_names.reverse()
+        return '/'.join(ancestor_tag_names)
+
+    def _get_schema_attributes(self):
+        """
+            Gets the schema attributes form the XML root node
+
+        Returns
+        -------
+        attribute_dict: {str: str}
+
+        """
+        return self._root_element.attrib
+
+    def _get_prologue(self):
+        prologue_elements = self._get_elements_by_name(self.PROLOGUE_ELEMENT)
+        if len(prologue_elements) == 1:
+            return prologue_elements[0].text
+        return ""
+
+    def _get_epilogue(self):
+        epilogue_elements = self._get_elements_by_name(self.EPILOGUE_ELEMENT)
+        if len(epilogue_elements) == 1:
+            return epilogue_elements[0].text
+        return ""
+
+    def _get_tags_by_attribute(self, attribute_name):
+        """Gets the tag that have a specific attribute.
+
+        Parameters
+        ----------
+        attribute_name: str
+            The name of the attribute associated with the tags.
+
+        Returns
+        -------
+        tuple
+            A tuple containing tags and tag elements that have a specified attribute.
+
+        """
+        tags = []
+        tag_elements = self._root_element.findall('.//node[@%s]' % attribute_name)
+        for attribute_tag_element in tag_elements:
+            tag = self._get_tag_path_from_tag_element(attribute_tag_element)
+            tags.append(tag)
+        return tags, tag_elements
+
+    def _get_all_tags(self, tag_element_name='node'):
+        """Gets the tags that have a specific attribute.
+
+        Parameters
+        ----------
+        tag_element_name: str
+            The XML tag name of the tag elements. The default is 'node'.
+
+        Returns
+        -------
+        tuple
+            A tuple containing all the tags and tag elements in the XML file.
+
+        """
+        tags = []
+        tag_elements = self._root_element.findall('.//%s' % tag_element_name)
+        for tag_element in tag_elements:
+            tag = self._get_tag_path_from_tag_element(tag_element)
+            tags.append(tag)
+        return tags, tag_elements
+
+    def _get_elements_by_name(self, element_name='node', parent_element=None):
+        """Gets the elements that have a specific element name.
+
+        Parameters
+        ----------
+        element_name: str
+            The name of the element. The default is 'node'.
+        parent_element: RestrictedElement
+            The parent element. The default is 'None'. If a parent element is specified then only the children of the
+            parent will be returned with the given 'element_name'. If not specified the root element will be the parent.
+
+        Returns
+        -------
+        []
+            A list containing elements that have a specific element name.
+
+        """
+        if parent_element is None:
+            elements = self._root_element.findall('.//%s' % element_name)
+        else:
+            elements = parent_element.findall('.//%s' % element_name)
+        return elements
+
+    @staticmethod
+    def _get_element_desc(tags, tag_elements):
+        """
+            Create a dictionary of descriptions for the given tags and elements
+
+        Parameters
+        ----------
+        tags : [str]
+            The list of tags to get descriptions for
+        tag_elements : [Element]
+            The matching list of XML elements to get descriptions from
+        Returns
+        -------
+        desc_dict: {str: str}
+            {tag : description} for any tags that have one.
+        """
+        tags_desc = {}
+        for tag, tag_element in zip(tags, tag_elements):
+            for child in tag_element:
+                if child.tag == "description":
+                    tags_desc[tag] = child.text
+        return tags_desc
diff --git a/hedtools/hed/schema/xml2wiki.py b/hedtools/hed/schema/xml2wiki.py
deleted file mode 100644
index 18fee431e..000000000
--- a/hedtools/hed/schema/xml2wiki.py
+++ /dev/null
@@ -1,224 +0,0 @@
-from enum import Enum
-
-from hed.schema import constants
-from hed.util import file_util
-from hed.util.hed_schema import HedSchema
-from hed.schema.schema_validator import validate_schema
-
-
-class MainParseMode(Enum):
-    MainTags = 1
-    UnitClassTags = 2
-    UnitModifierTags = 3
-
-
-class HEDXml2Wiki:
-    def __init__(self):
-        self.parent_map = None
-        self.current_tag_string = ""
-        self.current_tag_extra = ""
-        self.parse_mode = MainParseMode.MainTags
-        self.output = []
-
-    def count_parent_nodes(self, node, tags_to_check=None):
-        """Count what depth this node should be considered, counting only tags in tags_to_check"""
-        if tags_to_check is None:
-            tags_to_check = ["node"]
-        nodes_in_parent = 0
-        parent_elem = node
-        while parent_elem in self.parent_map:
-            if parent_elem.tag in tags_to_check:
-                nodes_in_parent += 1
-            parent_elem = self.parent_map[parent_elem]
-
-        return nodes_in_parent
-
-    def flush_current_tag(self):
-        if self.current_tag_string or self.current_tag_extra:
-            if self.current_tag_extra:
-                new_tag = f"{self.current_tag_string} <nowiki>{self.current_tag_extra}</nowiki>"
-            else:
-                new_tag = self.current_tag_string
-            # print(new_tag)
-            self.output.append(new_tag)
-            self.current_tag_string = ""
-            self.current_tag_extra = ""
-
-    def add_blank_line(self):
-        self.output.append("")
-
-    def process_tree(self, hed_tree):
-        # Create a map so we can go from child to parent easily.
-        self.parent_map = {c: p for p in hed_tree.iter() for c in p}
-        self.current_tag_string = ""
-        self.current_tag_extra = ""
-        self.parse_mode = MainParseMode.MainTags
-        self.output = []
-
-        parse_mode = MainParseMode.MainTags
-        for elem in hed_tree.iter():
-            if elem.tag == "HED":
-                hed_attrib_string = self.get_attribs_from_root_hed_node(elem)
-                self.current_tag_string = f"HED {hed_attrib_string}"
-                self.flush_current_tag()
-                self.add_blank_line()
-                self.current_tag_string = "!# start hed"
-                self.flush_current_tag()
-                continue
-            elif elem.tag == "unitClasses":
-                self.flush_current_tag()
-                parse_mode = MainParseMode.UnitClassTags
-
-                section_text_name = "Unit classes"
-                self.current_tag_string += "\n"
-                self.current_tag_string += f"'''{section_text_name}'''"
-                self.add_blank_line()
-            elif elem.tag == "unitModifiers":
-                self.flush_current_tag()
-                parse_mode = MainParseMode.UnitModifierTags
-
-                section_text_name = "Unit modifiers"
-                self.current_tag_string += "\n"
-                self.current_tag_string += f"'''{section_text_name}'''"
-                # self.add_blank_line()
-
-            nodes_in_parent = None
-            if parse_mode == MainParseMode.MainTags:
-                nodes_in_parent = self.count_parent_nodes(elem) - 1
-                if elem.tag == "node":
-                    self.flush_current_tag()
-            elif parse_mode == MainParseMode.UnitClassTags:
-                nodes_in_parent = self.count_parent_nodes(elem,
-                                                          tags_to_check=["unitClasses", "units"])
-                if elem.tag == "unit" or elem.tag == "unitClass":
-                    self.flush_current_tag()
-
-                # Handle old style units where they don't have separate tags.
-                if elem.tag == "units" and not elem.text.isspace():
-                    self.flush_current_tag()
-                    unit_list = elem.text.split(',')
-                    for unit in unit_list:
-                        prefix = "*" * nodes_in_parent
-                        self.current_tag_string += f"{prefix} {unit}"
-                        self.flush_current_tag()
-
-            elif parse_mode == MainParseMode.UnitModifierTags:
-                nodes_in_parent = self.count_parent_nodes(elem, tags_to_check=["unitModifiers"])
-                if elem.tag == "unitModifier":
-                    self.flush_current_tag()
-
-            # stuff that applies to all modes
-            if elem.tag == "name" or elem.tag == "unit":
-                # handle special case where text is just "#"
-                if elem.text and "#" in elem.text:
-                    prefix = "*" * nodes_in_parent
-                    self.current_tag_string += f"{prefix}"
-                    self.current_tag_extra = f"{elem.text} {self.current_tag_extra}"
-                else:
-                    if nodes_in_parent == 0:
-                        self.current_tag_string += f"'''{elem.text}'''"
-                        self.add_blank_line()
-                    elif nodes_in_parent > 0:
-                        prefix = "*" * nodes_in_parent
-                        self.current_tag_string += f"{prefix} {elem.text}"
-                    elif nodes_in_parent == -1:
-                        self.current_tag_string += elem.tag
-
-            self.add_elem_desc(elem)
-            self.add_elem_attributes(elem)
-
-        self.flush_current_tag()
-        self.current_tag_string = "!# end hed"
-        self.flush_current_tag()
-
-        return self.output
-
-    def add_elem_desc(self, elem):
-        """Add description from passed in elem to current tag"""
-        if elem.tag == "description":
-            if self.current_tag_extra:
-                self.current_tag_extra += " "
-            self.current_tag_extra += f"[{elem.text}]"
-
-    def add_elem_attributes(self, elem):
-        """Add all attributes from the passed in elem to current tag"""
-        if len(elem.attrib) > 0:
-            self.current_tag_extra += "{"
-            is_first = True
-            sorted_keys = []
-            # This is purely optional, but makes comparing easier when it's identical
-            expected_key_order = ["takesValue", "isNumeric", "requireChild", "required", "unique",
-                                  "predicateType", "position", "unitClass", "default"]
-            for expected_key in expected_key_order:
-                if expected_key in elem.attrib:
-                    sorted_keys.append(expected_key)
-            for attrib_name in elem.attrib:
-                if attrib_name not in sorted_keys:
-                    sorted_keys.append(attrib_name)
-
-            for attrib_name in sorted_keys:
-                attrib_val = elem.attrib[attrib_name]
-                if attrib_name == "unitClass":
-                    unit_classes = attrib_val.split(",")
-                    for unit_class in unit_classes:
-                        if not is_first:
-                            self.current_tag_extra += ", "
-                        is_first = False
-                        self.current_tag_extra += f"{attrib_name}={unit_class}"
-                else:
-                    if not is_first:
-                        self.current_tag_extra += ", "
-                    is_first = False
-                    if attrib_val == "true":
-                        self.current_tag_extra += attrib_name
-                    elif attrib_val.isdigit():
-                        self.current_tag_extra += f"{attrib_name}={attrib_val}"
-                    else:
-                        self.current_tag_extra += f"{attrib_name}={attrib_val}"
-            self.current_tag_extra += "}"
-
-    @staticmethod
-    def get_attribs_from_root_hed_node(elem):
-        attrib_values = [f"{attr}:{elem.attrib[attr]}" for attr in constants.HED_VALID_ATTRIBUTES if
-                         attr in elem.attrib]
-        final_attrib_string = ", ".join(attrib_values)
-        return final_attrib_string
-
-
-def convert_hed_xml_2_wiki(hed_xml_url, local_xml_file=None, check_for_issues=True,
-                           display_filename=None):
-    """Converts the local HED xml file into a wikimedia file
-
-    Parameters
-    ----------
-    hed_xml_url: str or None
-        url pointing to the .xml file to use
-    local_xml_file: str or None
-        filepath to local xml hed schema(overrides hed_xml_url)
-    check_for_issues : bool
-        After conversion checks for warnings like capitalization or invalid characters.
-    display_filename: str
-        If present, will use this as the filename for context, rather than using the actual filename
-        Useful for temp filenames.
-    Returns
-    -------
-    mediawiki_filename: str
-        Location of output mediawiki file, None on complete failure
-    issues_list: [{}]
-        returns a list of error/warning dictionaries
-    """
-    if local_xml_file is None:
-        local_xml_file = file_util.url_to_file(hed_xml_url)
-
-    hed_xml_tree = HedSchema.parse_hed_xml_file(local_xml_file)
-
-    xml2wiki = HEDXml2Wiki()
-    output_strings = xml2wiki.process_tree(hed_xml_tree)
-    local_mediawiki_file = file_util.write_strings_to_file(output_strings, ".mediawiki")
-
-    issue_list = []
-    if check_for_issues:
-        warnings = validate_schema(local_xml_file, display_filename=display_filename)
-        issue_list += warnings
-
-    return local_mediawiki_file, issue_list
diff --git a/hedtools/hed/tools/map_tag_versions.py b/hedtools/hed/tools/map_tag_versions.py
index 6e5955311..531d83bc9 100644
--- a/hedtools/hed/tools/map_tag_versions.py
+++ b/hedtools/hed/tools/map_tag_versions.py
@@ -1,7 +1,9 @@
 import re
 import copy
 from hed.util.hed_string_util import split_hed_string, remove_slashes_and_spaces
-from hed.util.hed_schema import HedSchema, HedKey
+from hed import schema
+from hed.schema.hed_schema_constants import HedKey
+
 
 # Todo: Review this file once we have more stable test cases for it.
 
@@ -42,9 +44,9 @@ def read_version_map(version_map_filename, left_hed_schema=None, right_hed_schem
                      return_errors=False):
     left_dict = right_dict = None
     if left_hed_schema:
-        left_dict = HedSchema(left_hed_schema)
+        left_dict = schema.load_schema(left_hed_schema)
     if right_hed_schema:
-        right_dict = HedSchema(right_hed_schema)
+        right_dict = schema.load_schema(right_hed_schema)
     mapping_dict = {}
     error_list = []
 
diff --git a/hedtools/hed/tools/tag_format.py b/hedtools/hed/tools/tag_format.py
index 9bb1825dc..fb590db54 100644
--- a/hedtools/hed/tools/tag_format.py
+++ b/hedtools/hed/tools/tag_format.py
@@ -1,7 +1,7 @@
 from hed.util import error_reporter
 from hed.util.error_types import SchemaErrors, ErrorContext
 from hed.util import hed_string_util
-from hed.util.hed_schema import HedSchema
+from hed import schema
 
 
 class TagFormat:
@@ -9,20 +9,20 @@ class TagFormat:
     Class to convert hed3 tags between short and long form.
     """
 
-    def __init__(self, hed_xml_file=None, hed_schema=None, error_handler=None):
+    def __init__(self, hed_file=None, hed_schema=None, error_handler=None):
         """
 
         Parameters
         ----------
-        hed_xml_file : str
-            hed xml schema filepath to create HedSchema from
+        hed_file : str
+            hed xml/mediawiki schema filepath to create HedSchema from
         hed_schema : HedSchema, default None
              Used in place of hed_xml_file
         error_handler : ErrorHandler or None
             Used to report errors.  Uses a default one if none passed in.
         """
         if hed_schema is None:
-            hed_schema = HedSchema(hed_xml_file)
+            hed_schema = schema.load_schema(hed_file)
         if error_handler is None:
             error_handler = error_reporter.ErrorHandler()
         self._error_handler = error_handler
diff --git a/hedtools/hed/util/column_def_group.py b/hedtools/hed/util/column_def_group.py
index c4a6ced8e..bf279bdb5 100644
--- a/hedtools/hed/util/column_def_group.py
+++ b/hedtools/hed/util/column_def_group.py
@@ -279,24 +279,3 @@ def validate_entries(self, hed_schema=None, display_filename=None, error_handler
             error_handler.pop_error_context()
         return all_validation_issues
 
-    @staticmethod
-    def get_printable_issue_string(validation_issues, title=None, severity=None, skip_filename=True):
-        """Return a string with issues list flatted into single string, one per line
-
-        Parameters
-        ----------
-        validation_issues: []
-            Issues to print
-        title: str
-            Optional title that will always show up first if present(even if there are no validation issues)
-        severity: int
-            Return only warnings >= severity
-        skip_filename: bool
-            If true, don't add the filename context to the printable string.
-        Returns
-        -------
-        str
-            A str containing printable version of the issues or '[]'.
-
-        """
-        return error_reporter.ErrorHandler.get_printable_issue_string(validation_issues, title, severity, skip_filename)
diff --git a/hedtools/hed/util/error_reporter.py b/hedtools/hed/util/error_reporter.py
index 03763c2f1..e3fd8b201 100644
--- a/hedtools/hed/util/error_reporter.py
+++ b/hedtools/hed/util/error_reporter.py
@@ -356,147 +356,6 @@ def format_definition_error(self, error_type, def_name, tag_list=None):
         error_object_list = self._add_context_to_errors(error_object)
         return error_object_list
 
-    @staticmethod
-    def _get_context_from_issue(val_issue, skip_filename=True):
-        """
-        Extract all the context values from the given issue
-        Parameters
-        ----------
-        val_issue : {}
-            A dictionary a representing a single error
-        skip_filename: bool
-            If true, don't gather the filename context.
-        Returns
-        -------
-        context_list: []
-            A list of tuples containing the context_type and context for the given issue
-        """
-        single_issue_context = []
-        for key in val_issue:
-            if skip_filename and key == ErrorContext.FILE_NAME:
-                continue
-            if key in ErrorContext:
-                single_issue_context.append((key.value, *val_issue[key]))
-
-        return single_issue_context
-
-    @staticmethod
-    def _format_single_context_string(context_type, context, tab_count=0):
-        """
-        Takes a single context tuple and returns the human readable form.
-
-        Parameters
-        ----------
-        context_type : str
-            The context type of this entry
-        context : str
-            The value of this context
-        tab_count : int
-            Number of tabs to prefix each line with.
-
-        Returns
-        -------
-        context_string: str
-            A string containing the context, including tabs.
-        """
-        tab_string = tab_count * '\t'
-        error_types = {
-            ErrorContext.FILE_NAME.value: f"\nErrors in file '{context}'",
-            ErrorContext.SIDECAR_COLUMN_NAME.value: f"Column '{context}':",
-            ErrorContext.SIDECAR_KEY_NAME.value: f"Key: {context}",
-            ErrorContext.SIDECAR_HED_STRING.value: f"hed_string: {context}",
-            ErrorContext.ROW.value: f'Issues in row {context}:',
-            ErrorContext.COLUMN.value: f'Issues in column {context}:',
-            ErrorContext.CUSTOM_TITLE.value: context
-        }
-        context_portion = error_types[context_type]
-        context_string = f"{tab_string}{context_portion}\n"
-        return context_string
-
-    @staticmethod
-    def _get_context_string(single_issue_context, last_used_context):
-        """
-        Converts a single context list into the final human readable output form.
-
-        Parameters
-        ----------
-        single_issue_context : [()]
-            A list of tuples containing the context(context_type, context, increment_tab)
-        last_used_context : [()]
-            A list of tuples containing the last drawn context, so it can only add the parts that have changed.
-            This is always the same format as single_issue_context.
-
-        Returns
-        -------
-        context_string: str
-            The full string of context(potentially multiline) to add before the error
-        tab_string: str
-            The prefix to add to any message line with this context.
-        """
-        context_string = ""
-        tab_count = 0
-        found_difference = False
-        for i, context_tuple in enumerate(single_issue_context):
-            (context_type, context, increment_tab) = context_tuple
-            if len(last_used_context) > i and not found_difference:
-                last_drawn = last_used_context[i]
-                # Was drawn, and hasn't changed.
-                if last_drawn == context_tuple:
-                    if increment_tab:
-                        tab_count += 1
-                    continue
-
-            context_string += ErrorHandler._format_single_context_string(context_type, context, tab_count)
-            found_difference = True
-            if increment_tab:
-                tab_count += 1
-
-        tab_string = '\t' * tab_count
-        return context_string, tab_string
-
-    @staticmethod
-    def get_printable_issue_string(validation_issues, title=None, severity=None, skip_filename=True):
-        """Return a string with issues list flatted into single string, one per line
-
-        Parameters
-        ----------
-        validation_issues: []
-            Issues to print
-        title: str
-            Optional title that will always show up first if present(even if there are no validation issues)
-        severity: int
-            Return only warnings >= severity
-        skip_filename: bool
-            If true, don't add the filename context to the printable string.
-        Returns
-        -------
-        str
-            A str containing printable version of the issues or '[]'.
-
-        """
-        last_used_error_context = []
-
-        if severity is not None:
-            validation_issues = ErrorHandler.filter_issues_by_severity(validation_issues, severity)
-
-        issue_string = ""
-        for single_issue in validation_issues:
-            single_issue_context = ErrorHandler._get_context_from_issue(single_issue, skip_filename)
-            context_string, tab_string = ErrorHandler._get_context_string(single_issue_context, last_used_error_context)
-
-            issue_string += context_string
-            single_issue_message = tab_string + single_issue['message']
-            if "\n" in single_issue_message:
-                single_issue_message = single_issue_message.replace("\n", "\n" + tab_string)
-            issue_string += f"{single_issue_message}\n"
-            last_used_error_context = single_issue_context.copy()
-
-        if issue_string:
-            issue_string += "\n"
-        if title:
-            issue_string = title + '\n' + issue_string
-        return issue_string
-
     @staticmethod
     def filter_issues_by_severity(issues_list, severity):
         """
@@ -515,3 +374,144 @@ def filter_issues_by_severity(issues_list, severity):
             The list with all other severities removed.
         """
         return [issue for issue in issues_list if issue['severity'] <= severity]
+
+
+def get_printable_issue_string(validation_issues, title=None, severity=None, skip_filename=True):
+    """Return a string with issues list flatted into single string, one per line
+
+    Parameters
+    ----------
+    validation_issues: []
+        Issues to print
+    title: str
+        Optional title that will always show up first if present(even if there are no validation issues)
+    severity: int
+        Return only warnings >= severity
+    skip_filename: bool
+        If true, don't add the filename context to the printable string.
+    Returns
+    -------
+    str
+        A str containing printable version of the issues or '[]'.
+
+    """
+    last_used_error_context = []
+
+    if severity is not None:
+        validation_issues = ErrorHandler.filter_issues_by_severity(validation_issues, severity)
+
+    issue_string = ""
+    for single_issue in validation_issues:
+        single_issue_context = _get_context_from_issue(single_issue, skip_filename)
+        context_string, tab_string = _get_context_string(single_issue_context, last_used_error_context)
+
+        issue_string += context_string
+        single_issue_message = tab_string + single_issue['message']
+        if "\n" in single_issue_message:
+            single_issue_message = single_issue_message.replace("\n", "\n" + tab_string)
+        issue_string += f"{single_issue_message}\n"
+        last_used_error_context = single_issue_context.copy()
+
+    if issue_string:
+        issue_string += "\n"
+    if title:
+        issue_string = title + '\n' + issue_string
+    return issue_string
+
+
+def _get_context_from_issue(val_issue, skip_filename=True):
+    """
+    Extract all the context values from the given issue
+    Parameters
+    ----------
+    val_issue : {}
+        A dictionary a representing a single error
+    skip_filename: bool
+        If true, don't gather the filename context.
+    Returns
+    -------
+    context_list: []
+        A list of tuples containing the context_type and context for the given issue
+    """
+    single_issue_context = []
+    for key in val_issue:
+        if skip_filename and key == ErrorContext.FILE_NAME:
+            continue
+        if key.startswith("ec_"):
+            single_issue_context.append((key, *val_issue[key]))
+
+    return single_issue_context
+
+
+def _format_single_context_string(context_type, context, tab_count=0):
+    """
+    Takes a single context tuple and returns the human readable form.
+
+    Parameters
+    ----------
+    context_type : str
+        The context type of this entry
+    context : str
+        The value of this context
+    tab_count : int
+        Number of tabs to prefix each line with.
+
+    Returns
+    -------
+    context_string: str
+        A string containing the context, including tabs.
+    """
+    tab_string = tab_count * '\t'
+    error_types = {
+        ErrorContext.FILE_NAME: f"\nErrors in file '{context}'",
+        ErrorContext.SIDECAR_COLUMN_NAME: f"Column '{context}':",
+        ErrorContext.SIDECAR_KEY_NAME: f"Key: {context}",
+        ErrorContext.SIDECAR_HED_STRING: f"hed_string: {context}",
+        ErrorContext.ROW: f'Issues in row {context}:',
+        ErrorContext.COLUMN: f'Issues in column {context}:',
+        ErrorContext.CUSTOM_TITLE: context
+    }
+    context_portion = error_types[context_type]
+    context_string = f"{tab_string}{context_portion}\n"
+    return context_string
+
+
+def _get_context_string(single_issue_context, last_used_context):
+    """
+    Converts a single context list into the final human readable output form.
+
+    Parameters
+    ----------
+    single_issue_context : [()]
+        A list of tuples containing the context(context_type, context, increment_tab)
+    last_used_context : [()]
+        A list of tuples containing the last drawn context, so it can only add the parts that have changed.
+        This is always the same format as single_issue_context.
+
+    Returns
+    -------
+    context_string: str
+        The full string of context(potentially multiline) to add before the error
+    tab_string: str
+        The prefix to add to any message line with this context.
+    """
+    context_string = ""
+    tab_count = 0
+    found_difference = False
+    for i, context_tuple in enumerate(single_issue_context):
+        (context_type, context, increment_tab) = context_tuple
+        if len(last_used_context) > i and not found_difference:
+            last_drawn = last_used_context[i]
+            # Was drawn, and hasn't changed.
+            if last_drawn == context_tuple:
+                if increment_tab:
+                    tab_count += 1
+                continue
+
+        context_string += _format_single_context_string(context_type, context, tab_count)
+        found_difference = True
+        if increment_tab:
+            tab_count += 1
+
+    tab_string = '\t' * tab_count
+    return context_string, tab_string
diff --git a/hedtools/hed/util/error_types.py b/hedtools/hed/util/error_types.py
index 6dbe815da..12443deda 100644
--- a/hedtools/hed/util/error_types.py
+++ b/hedtools/hed/util/error_types.py
@@ -1,21 +1,19 @@
-from enum import Enum
-
 
 class ErrorSeverity:
     ERROR = 1
     WARNING = 2
 
 
-class ErrorContext(Enum):
+class ErrorContext:
     """Indicates the context this error took place in, each error potentially having multiple contexts"""
-    CUSTOM_TITLE = 'title'
-    FILE_NAME = 'filename'
-    SIDECAR_COLUMN_NAME = 'sidecarColumnName'
-    SIDECAR_KEY_NAME = 'sidecarKeyName'
-    SIDECAR_HED_STRING = 'sidecarHedString'
-    ROW = 'row'
-    COLUMN = 'column'
     # Use this one to display any passed in message without modification
+    CUSTOM_TITLE = 'ec_title'
+    FILE_NAME = 'ec_filename'
+    SIDECAR_COLUMN_NAME = 'ec_sidecarColumnName'
+    SIDECAR_KEY_NAME = 'ec_sidecarKeyName'
+    SIDECAR_HED_STRING = 'ec_sidecarHedString'
+    ROW = 'ec_row'
+    COLUMN = 'ec_column'
 
 
 class ValidationErrors:
diff --git a/hedtools/hed/util/exceptions.py b/hedtools/hed/util/exceptions.py
index 651577372..1ce7d86c7 100644
--- a/hedtools/hed/util/exceptions.py
+++ b/hedtools/hed/util/exceptions.py
@@ -5,6 +5,16 @@ class HedExceptions:
     CANNOT_PARSE_JSON = 'cannotParseJson'
     INVALID_EXTENSION = 'invalidExtension'
 
+    # These are actual schema issues, not that the file cannot be found or parsed
+    SCHEMA_HEADER_MISSING = 'schemaHeaderMissing',
+    SCHEMA_HEADER_INVALID = 'schemaHeaderInvalid',
+    BAD_HED_LIBRARY_NAME = 'badHedLibraryName',
+    BAD_HED_SEMANTIC_VERSION = 'BadHedSemanticVersion',
+    SCHEMA_START_MISSING = 'schemaStartMissing',
+    SCHEMA_END_INVALID = 'schemaEndMissing',
+    HED_END_INVALID = 'hedEndMissing'
+    INVALID_SECTION_SEPARATOR = 'invalidSectionSeparator'
+
 
 class HedFileError(Exception):
     """Exception raised when a file cannot be parsed due to being malformed, file IO, etc."""
@@ -44,9 +54,18 @@ def format_error_message(self, include_tabbing=True, return_string_only=False,
             HedExceptions.CANNOT_PARSE_XML: f"{error_prefix}Cannot parse schema XML: "
                                             f"{message}.  '{filename}'",
             HedExceptions.CANNOT_PARSE_JSON: f"{error_prefix}Cannot parse json: {message}. '{filename}'",
+            HedExceptions.SCHEMA_HEADER_MISSING: f"{error_prefix}{self.message}.  '{filename}'",
+            HedExceptions.SCHEMA_HEADER_INVALID: f"{error_prefix}{self.message}.  '{filename}'",
+            HedExceptions.BAD_HED_LIBRARY_NAME: f"{error_prefix}{self.message}.  '{filename}'",
+            HedExceptions.BAD_HED_SEMANTIC_VERSION: f"{error_prefix}{self.message}.  '{filename}'",
+            HedExceptions.SCHEMA_START_MISSING: f"{error_prefix}{self.message}.  '{filename}'",
+            HedExceptions.SCHEMA_END_INVALID: f"{error_prefix}{self.message}.  '{filename}'",
+            HedExceptions.HED_END_INVALID: f"{error_prefix}{self.message}.  '{filename}'",
+            HedExceptions.INVALID_SECTION_SEPARATOR: f"{error_prefix}{self.message}.  '{filename}'",
         }
         default_error_message = f'{error_prefix}Internal Error'
         error_message = error_types.get(error_type, default_error_message)
+        print(error_message)
 
         error_object = {'code': error_type,
                         'message': error_message}
diff --git a/hedtools/hed/util/hed_schema.py b/hedtools/hed/util/hed_schema.py
deleted file mode 100644
index f3914a6d7..000000000
--- a/hedtools/hed/util/hed_schema.py
+++ /dev/null
@@ -1,739 +0,0 @@
-"""
-This module contains the HedSchema class which encapsulates all HED tags, tag attributes, unit classes, and
-unit class attributes in a dictionary.
-
-The dictionary is a dictionary of dictionaries. The dictionary names are
-'default', 'extensionAllowed', 'isNumeric', 'position', 'predicateType', 'recommended', 'required', 'requireChild',
-'tags', 'takesValue', 'unique', 'units', and 'unitClass'.
-"""
-
-from defusedxml.ElementTree import parse
-import xml
-from hed.util.exceptions import HedFileError, HedExceptions
-
-
-# These need to match the attributes/element name/etc used to load from the xml
-class HedKey:
-    Default = 'default'
-    ExtensionAllowed = 'extensionAllowed'
-    IsNumeric = 'isNumeric'
-    Position = 'position'
-    PredicateType = 'predicateType'
-    Recommended = 'recommended'
-    RequiredPrefix = 'required'
-    RequireChild = 'requireChild'
-    AllTags = 'tags'
-    TakesValue = 'takesValue'
-    Unique = 'unique'
-    UnitClass = 'unitClass'
-
-    # Default Units for Type
-    DefaultUnits = 'defaultUnits'
-    Units = 'units'
-
-    SIUnit = 'SIUnit'
-    UnitSymbol = 'unitSymbol'
-
-    SIUnitModifier = 'SIUnitModifier'
-    SIUnitSymbolModifier = 'SIUnitSymbolModifier'
-
-    # If this is a valid HED3 spec, this allow mapping from short to long.
-    ShortTags = 'shortTags'
-
-
-class HedSchema:
-    TAG_DICTIONARY_KEYS = [HedKey.Default, HedKey.ExtensionAllowed, HedKey.IsNumeric, HedKey.Position,
-                           HedKey.PredicateType, HedKey.Recommended, HedKey.RequiredPrefix, HedKey.RequireChild,
-                           HedKey.AllTags, HedKey.TakesValue, HedKey.Unique, HedKey.UnitClass]
-    UNIT_CLASS_DICTIONARY_KEYS = [HedKey.SIUnit, HedKey.UnitSymbol]
-    UNIT_MODIFIER_DICTIONARY_KEYS = [HedKey.SIUnitModifier, HedKey.SIUnitSymbolModifier]
-
-    # These should mostly match the HedKey values above.
-    # These are repeated here for clarification primarily
-    DEFAULT_UNITS_FOR_TYPE_ATTRIBUTE = HedKey.DefaultUnits
-    DEFAULT_UNIT_FOR_OLD_UNIT_CLASS_ATTRIBUTE = HedKey.Default
-    UNIT_CLASS_ELEMENT = HedKey.UnitClass
-    UNIT_CLASS_UNIT_ELEMENT = 'unit'
-    UNIT_CLASS_UNITS_ELEMENT = HedKey.Units
-    UNIT_MODIFIER_ELEMENT = 'unitModifier'
-
-    VERSION_ATTRIBUTE = 'version'
-
-    def __init__(self, hed_xml_file_path):
-        """Constructor for the HedSchema class.
-
-        Parameters
-        ----------
-        hed_xml_file_path: str
-            The path to a HED XML file.
-        Returns
-        -------
-        HedSchema
-            A HedSchema object.
-
-        """
-        self.no_duplicate_tags = True
-        self.root_element = self.parse_hed_xml_file(hed_xml_file_path)
-        # Used to find parent elements of XML nodes for file parsing
-        self._parent_map = {c: p for p in self.root_element.iter() for c in p}
-        self._populate_dictionaries()
-
-    def get_root_element(self):
-        """Gets the root element of the HED XML file.
-
-        Parameters
-        ----------
-
-        Returns
-        -------
-        Element
-            The root element of the HED XML file.
-
-        """
-        return self.root_element
-
-    def get_dictionaries(self):
-        """Gets a dictionary of dictionaries that contains all of the tags, tag attributes, unit class units, and unit
-           class attributes
-
-        Parameters
-        ----------
-
-        Returns
-        -------
-        {}
-            A dictionary of dictionaries that contains all of the tags, tag attributes, unit class units, and unit
-            class attributes
-
-        """
-        return self.dictionaries
-
-    def has_duplicate_tags(self):
-        """
-        Returns True if this is a valid hed3 schema with no duplicate short tags.
-
-        Returns
-        -------
-        bool
-            Returns True if this is a valid hed3 schema with no duplicate short tags.
-        """
-        return not self.no_duplicate_tags
-
-    def _populate_dictionaries(self):
-        """Populates a dictionary of dictionaries that contains all of the tags, tag attributes, unit class units,
-           and unit class attributes.
-
-        Parameters
-        ----------
-
-        Returns
-        -------
-        {}
-            A dictionary of dictionaries that contains all of the tags, tag attributes, unit class units, and unit class
-            attributes.
-
-        """
-        self.dictionaries = {}
-        self._populate_tag_dictionaries()
-        self._populate_unit_class_dictionaries()
-        self._populate_unit_modifier_dictionaries()
-        self._populate_short_tag_dict()
-        self._add_hed3_compatible_tags()
-
-    @property
-    def short_tag_mapping(self):
-        """
-        This returns the short->long tag dictionary if we have a hed3 compatible schema.
-
-        Returns
-        -------
-        short_tag_dict: {}
-            Returns the short tag mapping dictionary, or None if this is not a hed3 compatible schema.
-        """
-        if self.no_duplicate_tags:
-            return self.dictionaries[HedKey.ShortTags]
-        return None
-
-    def get_all_forms_of_tag(self, short_tag_to_check):
-        """
-        Given a short tag, return all the longer versions of it.
-
-        eg: "definition" will return
-                ["definition", "informational/definition", "attribute/informational/definition"]
-
-        Parameters
-        ----------
-        short_tag_to_check : str
-            The short version of a hed tag we are interested in.
-
-        Returns
-        -------
-        tag_versions: [str]
-            A list of all short, intermediate, and long versions of the passed in short tag.
-            Returns empty list if no versions found.
-        """
-        try:
-            tag_entry = self.short_tag_mapping[short_tag_to_check.lower()]
-        except KeyError:
-            return []
-
-        split_tags = tag_entry.lower().split("/")
-        final_tag = ""
-        all_forms = []
-        for tag in reversed(split_tags):
-            final_tag = tag + "/" + final_tag
-            all_forms.append(final_tag)
-
-        return all_forms
-
-    def _populate_tag_dictionaries(self):
-        """Populates a dictionary of dictionaries associated with tags and their attributes.
-
-        Parameters
-        ----------
-
-        Returns
-        -------
-        {}
-            A dictionary of dictionaries that has been populated with dictionaries associated with tag attributes.
-
-        """
-        for dict_key in HedSchema.TAG_DICTIONARY_KEYS:
-            tags, tag_elements = self.get_tags_by_attribute(dict_key)
-            if HedKey.ExtensionAllowed == dict_key:
-                child_tags = self._get_all_child_tags(tag_elements)
-                child_tags_dictionary = self._string_list_to_lowercase_dictionary(child_tags)
-                tag_dictionary = self._string_list_to_lowercase_dictionary(tags)
-                tag_dictionary.update(child_tags_dictionary)
-            elif HedKey.Default == dict_key or \
-                    HedKey.UnitClass == dict_key:
-                tag_dictionary = self._populate_tag_to_attribute_dictionary(tags, tag_elements, dict_key)
-            elif HedKey.AllTags == dict_key:
-                tags = self.get_all_tags()[0]
-                tag_dictionary = self._string_list_to_lowercase_dictionary(tags)
-            else:
-                tag_dictionary = self._string_list_to_lowercase_dictionary(tags)
-            self.dictionaries[dict_key] = tag_dictionary
-
-    def _populate_unit_class_dictionaries(self):
-        """Populates a dictionary of dictionaries associated with all of the unit classes, unit class units, and unit
-           class default units.
-
-        Parameters
-        ----------
-
-        Returns
-        -------
-        dict
-            A dictionary of dictionaries associated with all of the unit classes, unit class units, and unit class
-            default units.
-
-        """
-        unit_class_elements = self._get_elements_by_name(self.UNIT_CLASS_ELEMENT)
-        if len(unit_class_elements) == 0:
-            self.has_unit_classes = False
-            return
-        self.has_unit_classes = True
-        self._populate_unit_class_default_unit_dictionary(unit_class_elements)
-        self._populate_unit_class_units_dictionary(unit_class_elements)
-
-    def _populate_unit_modifier_dictionaries(self):
-        """
-        Gathers all unit modifier definitions from the schema.
-
-        Returns
-        -------
-
-        """
-        unit_modifier_elements = self._get_elements_by_name(self.UNIT_MODIFIER_ELEMENT)
-        if len(unit_modifier_elements) == 0:
-            self.has_unit_modifiers = False
-            return
-        self.has_unit_modifiers = True
-        for unit_modifier_key in self.UNIT_MODIFIER_DICTIONARY_KEYS:
-            self.dictionaries[unit_modifier_key] = {}
-        for unit_modifier_element in unit_modifier_elements:
-            unit_modifier_name = self._get_element_tag_value(unit_modifier_element)
-            for unit_modifier_key in self.UNIT_MODIFIER_DICTIONARY_KEYS:
-                self.dictionaries[unit_modifier_key][unit_modifier_name] = unit_modifier_element.get(unit_modifier_key)
-
-    def find_duplicate_tags(self):
-        """Finds all tags that are not unique.
-
-        Returns
-        -------
-        duplicate_tag_dict: {str: [str]}
-            A dictionary of all duplicate short tags as keys, with the values being a list of
-            long tags sharing that short tag
-        """
-        duplicate_dict = {}
-        short_tag_dict = self.dictionaries[HedKey.ShortTags]
-        for tag_name in short_tag_dict:
-            modified_name = f'{tag_name}'
-            if isinstance(short_tag_dict[tag_name], list):
-                duplicate_dict[modified_name] = short_tag_dict[tag_name]
-
-        return duplicate_dict
-
-    def dupe_tag_iter(self, return_detailed_info=False):
-        """
-        An iterator that goes over each line of the duplicate tags dict, including descriptive ones.
-
-        Parameters
-        ----------
-        return_detailed_info : bool
-            If true, also returns header lines listing the number of duplicate tags for each short tag.
-
-        Yields
-        -------
-        text_line: str
-            A list of long tags that have duplicates, with optional descriptive short tag lines.
-        """
-        duplicate_dict = self.find_duplicate_tags()
-        prefix_string = ""
-        if return_detailed_info:
-            prefix_string = "\t"
-        for tag_name in duplicate_dict:
-            if return_detailed_info:
-                yield f"Duplicate tag found {tag_name} - {len(duplicate_dict[tag_name])} versions:"
-            for tag_entry in duplicate_dict[tag_name]:
-                yield f"{prefix_string}{tag_entry}"
-
-    def _populate_short_tag_dict(self):
-        """
-        Create a mapping from the short version of a tag to the long version and
-        determines if this is a hed3 compatible schema.
-
-        Returns
-        -------
-        """
-        self.no_duplicate_tags = True
-        base_tag_dict = self.dictionaries[HedKey.AllTags]
-        new_short_tag_dict = {}
-        for tag, unformatted_tag in base_tag_dict.items():
-            split_tags = unformatted_tag.split("/")
-            short_tag = split_tags[-1]
-            if short_tag == "#":
-                continue
-            short_clean_tag = short_tag.lower()
-            new_tag_entry = unformatted_tag
-            if short_clean_tag not in new_short_tag_dict:
-                new_short_tag_dict[short_clean_tag] = new_tag_entry
-            else:
-                self.no_duplicate_tags = False
-                if not isinstance(new_short_tag_dict[short_clean_tag], list):
-                    new_short_tag_dict[short_clean_tag] = [new_short_tag_dict[short_clean_tag]]
-                new_short_tag_dict[short_clean_tag].append(new_tag_entry)
-        self.dictionaries[HedKey.ShortTags] = new_short_tag_dict
-
-    def _add_hed3_compatible_tags(self):
-        """
-        Updates the normal tag dictionaries with all the intermediate forms if this is a hed3 compatible schema."
-
-        Returns
-        -------
-        """
-        if self.no_duplicate_tags:
-            for dict_key in self.TAG_DICTIONARY_KEYS:
-                tag_dictionary = self.dictionaries[dict_key]
-                new_entries = {}
-                for full_tag, value in tag_dictionary.items():
-                    split_tags = full_tag.split("/")
-                    final_tag = ""
-                    for tag in reversed(split_tags):
-                        final_tag = tag + "/" + final_tag
-                        # We need to include the #, but make sure we don't create a tag with just # alone.
-                        if tag == "#":
-                            continue
-                        # Remove extra trailing slash.
-                        new_entries[final_tag[:-1]] = value
-                tag_dictionary.update(new_entries)
-
-    def _populate_unit_class_units_dictionary(self, unit_class_elements):
-        """Populates a dictionary that contains unit class units.
-
-        Parameters
-        ----------
-        unit_class_elements: list
-            A list of unit class elements.
-
-        Returns
-        -------
-        dict
-            A dictionary that contains all the unit class units.
-
-        """
-        self.dictionaries[HedKey.Units] = {}
-        for unit_class_key in self.UNIT_CLASS_DICTIONARY_KEYS:
-            self.dictionaries[unit_class_key] = {}
-        for unit_class_element in unit_class_elements:
-            element_name = self._get_element_tag_value(unit_class_element)
-            element_units = self._get_elements_by_name(self.UNIT_CLASS_UNIT_ELEMENT, unit_class_element)
-            if not element_units:
-                element_units = self._get_element_tag_value(unit_class_element, self.UNIT_CLASS_UNITS_ELEMENT)
-                units = element_units.split(',')
-                units_list = list(map(lambda unit: unit.lower(), units))
-                self.dictionaries[HedKey.Units][element_name] = units_list
-                continue
-            element_unit_names = list(map(lambda element: element.text, element_units))
-            self.dictionaries[HedKey.Units][element_name] = element_unit_names
-            for element_unit in element_units:
-                unit_name = element_unit.text
-                for unit_class_key in self.UNIT_CLASS_DICTIONARY_KEYS:
-                    self.dictionaries[unit_class_key][unit_name] = element_unit.get(unit_class_key)
-
-    def _populate_unit_class_default_unit_dictionary(self, unit_class_elements):
-        """Populates a dictionary that contains unit class default units.
-
-        Parameters
-        ----------
-        unit_class_elements: list
-            A list of unit class elements.
-
-        Returns
-        -------
-        dict
-            A dictionary that contains all the unit class default units.
-
-        """
-        self.dictionaries[HedKey.DefaultUnits] = {}
-        for unit_class_element in unit_class_elements:
-            unit_class_element_name = self._get_element_tag_value(unit_class_element)
-            default_unit = unit_class_element.get(self.DEFAULT_UNITS_FOR_TYPE_ATTRIBUTE)
-            if default_unit is None:
-                self.dictionaries[HedKey.DefaultUnits][unit_class_element_name] = \
-                    unit_class_element.attrib[HedSchema.DEFAULT_UNIT_FOR_OLD_UNIT_CLASS_ATTRIBUTE]
-            else:
-                self.dictionaries[HedKey.DefaultUnits][unit_class_element_name] = default_unit
-
-    def _populate_tag_to_attribute_dictionary(self, tag_list, tag_element_list, attribute_name):
-        """Populates the dictionaries associated with default unit tags in the attribute dictionary.
-
-        Parameters
-        ----------
-        tag_list: []
-            A list containing tags that have a specific attribute.
-        tag_element_list: []
-            A list containing tag elements that have a specific attribute.
-        attribute_name: str
-            The name of the attribute associated with the tags and tag elements.
-
-        Returns
-        -------
-        {}
-            The attribute dictionary that has been populated with dictionaries associated with tags.
-
-        """
-        dictionary = {}
-        for index, tag in enumerate(tag_list):
-            dictionary[tag.lower()] = tag_element_list[index].attrib[attribute_name]
-        return dictionary
-
-    def _string_list_to_lowercase_dictionary(self, string_list):
-        """Converts a string list into a dictionary. The keys in the dictionary will be the lowercase values of the
-           strings in the list.
-
-        Parameters
-        ----------
-        string_list: list
-            A list containing string elements.
-
-        Returns
-        -------
-        dict
-            A dictionary containing the strings in the list.
-
-        """
-        lowercase_dictionary = {}
-        for string_element in string_list:
-            lowercase_dictionary[string_element.lower()] = string_element
-        return lowercase_dictionary
-
-    @staticmethod
-    def parse_hed_xml_file(hed_xml_file_path):
-        """Parses a XML file and returns the root element.
-
-        Parameters
-        ----------
-        hed_xml_file_path: str
-            The path to a HED XML file.
-
-        Returns
-        -------
-        RestrictedElement
-            The root element of the HED XML file.
-
-        """
-        try:
-            hed_xml_tree = parse(hed_xml_file_path)
-        except xml.etree.ElementTree.ParseError as e:
-            raise HedFileError(HedExceptions.CANNOT_PARSE_XML, e.msg, hed_xml_file_path)
-        except FileNotFoundError as e:
-            raise HedFileError(HedExceptions.FILE_NOT_FOUND, e.strerror, hed_xml_file_path)
-        except TypeError as e:
-            raise HedFileError(HedExceptions.FILE_NOT_FOUND, str(e), hed_xml_file_path)
-        return hed_xml_tree.getroot()
-
-    def _get_ancestor_tag_names(self, tag_element):
-        """Gets all the ancestor tag names of a tag element.
-
-        Parameters
-        ----------
-        tag_element: Element
-            A tag element in the HED XML file.
-
-        Returns
-        -------
-        []
-            A list containing all of the ancestor tag names of a given tag.
-
-        """
-        ancestor_tags = []
-        parent_tag_name = self._get_parent_tag_name(tag_element)
-        parent_element = self._parent_map[tag_element]
-        while parent_tag_name:
-            ancestor_tags.append(parent_tag_name)
-            parent_tag_name = self._get_parent_tag_name(parent_element)
-            if parent_tag_name:
-                parent_element = self._parent_map[parent_element]
-        return ancestor_tags
-
-    def _get_element_tag_value(self, element, tag_name='name'):
-        """Gets the value of the element's tag.
-
-        Parameters
-        ----------
-        element: Element
-            A element in the HED XML file.
-        tag_name: str
-            The name of the XML element's tag. The default is 'name'.
-
-        Returns
-        -------
-        str
-            The value of the element's tag. If the element doesn't have the tag then it will return an empty string.
-
-        """
-        return element.find(tag_name).text
-
-    def _get_parent_tag_name(self, tag_element):
-        """Gets the name of the tag parent element.
-
-        Parameters
-        ----------
-        tag_element: Element
-            A tag element in the HED XML file.
-
-        Returns
-        -------
-        str
-            The name of the tag element's parent. If there is no parent tag then an empty string is returned.
-
-        """
-        parent_tag_element = self._parent_map[tag_element]
-        if parent_tag_element is not None:
-            return parent_tag_element.findtext('name')
-        else:
-            return ''
-
-    def _get_tag_path_from_tag_element(self, tag_element):
-        """Gets the tag path from a given tag element.
-
-        Parameters
-        ----------
-        tag_element: Element
-            A tag element in the HED XML file.
-
-        Returns
-        -------
-        str
-            A tag path which is typically referred to as a tag. The tag and it's ancestor tags will be separated by /'s.
-
-        """
-        ancestor_tag_names = self._get_ancestor_tag_names(tag_element)
-        ancestor_tag_names.insert(0, self._get_element_tag_value(tag_element))
-        ancestor_tag_names.reverse()
-        return '/'.join(ancestor_tag_names)
-
-    def get_tags_by_attribute(self, attribute_name):
-        """Gets the tag that have a specific attribute.
-
-        Parameters
-        ----------
-        attribute_name: str
-            The name of the attribute associated with the tags.
-
-        Returns
-        -------
-        tuple
-            A tuple containing tags and tag elements that have a specified attribute.
-
-        """
-        tags = []
-        tag_elements = self.root_element.findall('.//node[@%s]' % attribute_name)
-        for attribute_tag_element in tag_elements:
-            tag = self._get_tag_path_from_tag_element(attribute_tag_element)
-            tags.append(tag)
-        return tags, tag_elements
-
-    def get_all_tags(self, tag_element_name='node'):
-        """Gets the tags that have a specific attribute.
-
-        Parameters
-        ----------
-        tag_element_name: str
-            The XML tag name of the tag elements. The default is 'node'.
-
-        Returns
-        -------
-        tuple
-            A tuple containing all the tags and tag elements in the XML file.
-
-        """
-        tags = []
-        tag_elements = self.root_element.findall('.//%s' % tag_element_name)
-        for tag_element in tag_elements:
-            tag = self._get_tag_path_from_tag_element(tag_element)
-            tags.append(tag)
-        return tags, tag_elements
-
-    # def _get_elements_by_attribute(self, attribute_name, element_name='node'):
-    #     """Gets the elements that have a specific attribute.
-    #
-    #     Parameters
-    #     ----------
-    #     attribute_name: str
-    #         The name of the attribute associated with the element.
-    #     element_name: str
-    #         The name of the XML element tag name. The default is 'node'.
-    #
-    #     Returns
-    #     -------
-    #     list
-    #         A list containing elements that have a specified attribute.
-    #
-    #     """
-    #     tag_elements = self.root_element.xpath('.//%s[@%s]' % (element_name, attribute_name))
-    #     return tag_elements
-
-    def get_all_descriptions(self):
-        """
-        Gather all description nodes from the xml file and pair them with their tag name.
-
-        Returns
-        -------
-        {str: str}:
-            tag name : description
-        """
-        elements = self._get_elements_by_name("description")
-        parents = [self._get_parent_tag_name(element) for element in elements]
-        final_dict = {parent: element.text for parent, element in zip(parents, elements)}
-        return final_dict
-
-    def get_all_terms(self):
-        """
-        Gets a single copy of all hed terms from the schema, for hed2 or hed3 compatible.
-
-        Returns
-        -------
-        term_list: [str]
-            A list of all terms(short tags) from the schema.
-        """
-        final_list = []
-        if not self.has_duplicate_tags():
-            for lower_tag, org_tag in self.short_tag_mapping.items():
-                final_list.append(org_tag.split('/')[-1])
-        # Fallback for hed2 style schema validation
-        else:
-            for lower_tag, org_tag in self.dictionaries[HedKey.AllTags].items():
-                final_list.append(org_tag.split('/')[-1])
-        return final_list
-
-    def _get_elements_by_name(self, element_name='node', parent_element=None):
-        """Gets the elements that have a specific element name.
-
-        Parameters
-        ----------
-        element_name: str
-            The name of the element. The default is 'node'.
-        parent_element: RestrictedElement
-            The parent element. The default is 'None'. If a parent element is specified then only the children of the
-            parent will be returned with the given 'element_name'. If not specified the root element will be the parent.
-
-        Returns
-        -------
-        []
-            A list containing elements that have a specific element name.
-
-        """
-        if parent_element is None:
-            elements = self.root_element.findall('.//%s' % element_name)
-        else:
-            elements = parent_element.findall('.//%s' % element_name)
-        return elements
-
-    def _get_all_child_tags(self, tag_elements=None, element_name='node', exclude_take_value_tags=True):
-        """Gets the tag elements that are children of the given nodes
-
-        Parameters
-        ----------
-        tag_elements: list of nodes
-            The list to return all child tags from
-        element_name: str
-            The name of the XML tag elements. The default is 'node'.
-        exclude_take_value_tags: bool
-            True if to exclude tags that take values. False, if otherwise. The default is True.
-
-        Returns
-        -------
-        list
-            A list containing the tags that are child nodes.
-
-        """
-        child_tags = []
-        if tag_elements is None:
-            tag_elements = self._get_elements_by_name(element_name)
-        for tag_element in tag_elements:
-            tag_element_children = self._get_elements_by_name(element_name, tag_element)
-            for child_tag_element in tag_element_children:
-                tag = self._get_tag_path_from_tag_element(child_tag_element)
-                if exclude_take_value_tags and tag[-1] == '#':
-                    continue
-                child_tags.append(tag)
-        return child_tags
-
-    def tag_has_attribute(self, tag, tag_attribute):
-        """Checks to see if the tag has a specific attribute.
-
-        Parameters
-        ----------
-        tag: str
-            A tag.
-        tag_attribute: str
-            A tag attribute.
-        Returns
-        -------
-        bool
-            True if the tag has the specified attribute. False, if otherwise.
-
-        """
-        if self.dictionaries[tag_attribute].get(tag.lower()):
-            return True
-        return False
-
-    @staticmethod
-    def get_hed_xml_version(hed_xml_file_path):
-        """Gets the version number from a HED XML file.
-
-        Parameters
-        ----------
-        hed_xml_file_path: str
-            The path to a HED XML file.
-        Returns
-        -------
-        str
-            The version number of the HED XML file.
-
-        """
-        root_node = HedSchema.parse_hed_xml_file(hed_xml_file_path)
-        return root_node.attrib[HedSchema.VERSION_ATTRIBUTE]
diff --git a/hedtools/hed/validator/hed_validator.py b/hedtools/hed/validator/hed_validator.py
index 5a608b3af..068233af5 100644
--- a/hedtools/hed/validator/hed_validator.py
+++ b/hedtools/hed/validator/hed_validator.py
@@ -7,11 +7,10 @@
 from hed.util.error_types import ErrorContext
 from hed.util import hed_cache
 from hed.util import error_reporter
-from hed.util.hed_schema import HedSchema
+from hed import schema
 from hed.util.hed_string_delimiter import HedStringDelimiter
 from hed.validator.tag_validator import TagValidator
 from hed.util.hed_file_input import BaseFileInput
-from hed.util.exceptions import HedFileError, HedExceptions
 
 
 class HedValidator:
@@ -135,7 +134,7 @@ def _get_hed_schema(hed_xml_file, get_specific_version=None):
         if hed_xml_file is None and get_specific_version is None:
             hed_cache.cache_all_hed_xml_versions()
         final_hed_xml_file = hed_cache.get_local_file(hed_xml_file, get_specific_version)
-        hed_schema = HedSchema(final_hed_xml_file)
+        hed_schema = schema.load_schema(final_hed_xml_file)
         return hed_schema
 
     def _validate_hed_tags_in_file(self, hed_input):
@@ -371,28 +370,6 @@ def _validate_individual_tags_in_hed_string(self, hed_string_delimiter):
                                                                   previous_formatted_tag=previous_formatted_tag)
         return validation_issues
 
-    @staticmethod
-    def get_printable_issue_string(validation_issues, title=None, severity=None, skip_filename=True):
-        """Return a string with issues list flatted into single string, one per line
-
-        Parameters
-        ----------
-        validation_issues: []
-            Issues to print
-        title: str
-            Optional title that will always show up first if present(even if there are no validation issues)
-        severity: int
-            Return only warnings >= severity
-        skip_filename: bool
-            If true, don't add the filename context to the printable string.
-        Returns
-        -------
-        str
-            A str containing printable version of the issues or '[]'.
-
-        """
-        return error_reporter.ErrorHandler.get_printable_issue_string(validation_issues, title, severity, skip_filename)
-
     @staticmethod
     def get_previous_original_and_formatted_tag(original_and_formatted_tags, loop_index):
         """Retrieves the previous original and formatted tag from a list of tuples.
diff --git a/hedtools/hed/validator/tag_validator.py b/hedtools/hed/validator/tag_validator.py
index d2d854e03..48e937b93 100644
--- a/hedtools/hed/validator/tag_validator.py
+++ b/hedtools/hed/validator/tag_validator.py
@@ -9,7 +9,7 @@
 
 from hed.util.error_types import ValidationErrors, ValidationWarnings
 from hed.util import error_reporter
-from hed.util.hed_schema import HedSchema, HedKey
+from hed.schema import HedKey
 
 pluralize = inflect.engine()
 pluralize.defnoun("hertz", "hertz")
@@ -52,7 +52,7 @@ def __init__(self, hed_schema=None, check_for_warnings=False, run_semantic_valid
         self._error_handler = error_handler
         self._hed_schema = hed_schema
         if hed_schema:
-            self._hed_schema_dictionaries = hed_schema.get_dictionaries()
+            self._hed_schema_dictionaries = hed_schema.dictionaries
         else:
             self._hed_schema_dictionaries = None
         self._check_for_warnings = check_for_warnings
@@ -259,15 +259,14 @@ def is_extension_allowed_tag(self, formatted_tag):
             The tag that is used to do the validation.
         Returns
         -------
-        bool
+        tag_takes_extension: bool
             True if the tag has the 'extensionAllowed' attribute. False, if otherwise.
-
         """
         tag_slash_indices = self.get_tag_slash_indices(formatted_tag)
         for tag_slash_index in tag_slash_indices:
             tag_substring = self.get_tag_substring_by_end_index(formatted_tag, tag_slash_index)
             if self._hed_schema.tag_has_attribute(tag_substring,
-                                                  HedKey.ExtensionAllowed):
+                                                  HedKey.ExtensionAllowedPropagated):
                 return True
         return False
 
diff --git a/hedweb/hed/web/constants/blueprint_constants.py b/hedweb/hed/web/constants/blueprint_constants.py
deleted file mode 100644
index 2e0f69355..000000000
--- a/hedweb/hed/web/constants/blueprint_constants.py
+++ /dev/null
@@ -1,1 +0,0 @@
-ROUTE_BLUEPRINT = 'route_blueprint'
diff --git a/hedweb/hed/web/constants/file_constants.py b/hedweb/hed/web/constants/file_constants.py
index 2b3930143..9b565a556 100644
--- a/hedweb/hed/web/constants/file_constants.py
+++ b/hedweb/hed/web/constants/file_constants.py
@@ -3,4 +3,4 @@
 DICTIONARY_FILE_EXTENSIONS = ['.json']
 SCHEMA_WIKI_EXTENSION = '.mediawiki'
 SCHEMA_XML_EXTENSION = '.xml'
-SCHEMA_EXTENSIONS = [SCHEMA_XML_EXTENSION, SCHEMA_WIKI_EXTENSION]
\ No newline at end of file
+SCHEMA_EXTENSIONS = [SCHEMA_XML_EXTENSION, SCHEMA_WIKI_EXTENSION]
diff --git a/hedweb/hed/web/constants/route_constants.py b/hedweb/hed/web/constants/route_constants.py
index 5883c945f..9e3ad3279 100644
--- a/hedweb/hed/web/constants/route_constants.py
+++ b/hedweb/hed/web/constants/route_constants.py
@@ -1,3 +1,5 @@
+ROUTE_BLUEPRINT = 'route_blueprint'
+
 ADDITIONAL_EXAMPLES_ROUTE = '/additional-examples'
 COMMON_ERRORS_ROUTE = '/common-errors'
 DELETE_FILE_ROUTE = '/delete/<filename>'
diff --git a/hedweb/hed/web/dictionary.py b/hedweb/hed/web/dictionary.py
index 57caece50..ce3c4eac2 100644
--- a/hedweb/hed/web/dictionary.py
+++ b/hedweb/hed/web/dictionary.py
@@ -1,17 +1,14 @@
-import json
-import traceback
 from urllib.error import URLError, HTTPError
 from flask import current_app
 
-from hed.util.file_util import get_file_extension, delete_file_if_it_exists, write_strings_to_file
-from hed.validator.hed_validator import HedValidator
+from hed.schema import HedSchema, load_schema
+from hed.util.error_reporter import get_printable_issue_string
+from hed.util.file_util import delete_file_if_it_exists
 from hed.util.column_def_group import ColumnDefGroup
-from hed.util.hed_schema import HedSchema
-
 from hed.web.constants import common_constants, error_constants, file_constants
 from hed.web.web_utils import generate_filename, generate_download_file_response, \
-    handle_http_error, get_hed_path_from_pull_down, get_printable_issue_string, \
-    get_uploaded_file_path_from_form, save_file_to_upload_folder, save_text_to_upload_folder, get_optional_form_field
+    handle_http_error, get_hed_path_from_pull_down, \
+    get_uploaded_file_path_from_form, save_text_to_upload_folder, get_optional_form_field
 
 app_config = current_app.config
 
@@ -45,52 +42,6 @@ def generate_arguments_from_dictionary_form(request):
     return input_arguments
 
 
-def report_eeg_events_validation_status(request):
-    """Reports validation status of hed strings associated with EEG events
-       received from EEGLAB plugin HEDTools
-
-    Parameters
-    ----------
-    request: Request object
-        A Request object containing user data submitted by HEDTools.
-        Keys include "hed_strings", "check_for_warnings", and "hed_xml_file"
-
-    Returns
-    -------
-    string
-        A serialized JSON string containing information related to the hed strings validation result.
-        If the validation fails then a 500 error message is returned.
-    """
-    validation_status = {}
-
-    # Parse uploaded data
-    form_data = request.form
-    check_for_warnings = form_data["check_for_warnings"] == '1' if "check_for_warnings" in form_data else False
-    # if hed_xml_file was submitted, it's accessed by request.files, otherwise empty
-    if "hed-xml-file" in request.files and get_file_extension(request.files["hed-xml-file"].filename) == "xml":
-        hed_xml_file = save_file_to_upload_folder(request.files["hed-xml-file"])
-    else:
-        hed_xml_file = ''
-
-    try:
-        # parse hed_strings from json
-        hed_strings = json.loads(form_data["hed-strings"])
-        # hed_strings is a list of HED strings associated with events in EEG.event (order preserved)
-        hed_input_reader = HedValidator(hed_strings, check_for_warnings=check_for_warnings, hed_xml_file=hed_xml_file)
-        # issues is a list of lists. Element list is empty if no error,
-        # else is a list of dictionaries, each dictionary contains an error-message key-value pair
-        issues = hed_input_reader.get_validation_issues()
-
-        # Prepare response
-        validation_status["issues"] = issues
-    except:
-        validation_status[error_constants.ERROR_KEY] = traceback.format_exc()
-    finally:
-        delete_file_if_it_exists(hed_xml_file)
-
-    return validation_status
-
-
 def report_dictionary_validation_status(request):
     """Reports the spreadsheet validation status.
 
@@ -108,7 +59,7 @@ def report_dictionary_validation_status(request):
     input_arguments = []
     try:
         input_arguments = generate_arguments_from_dictionary_form(request)
-        return validate_dictionary(input_arguments, hed_schema=None)
+        return validate_dictionary(input_arguments)
     except HTTPError:
         return error_constants.NO_URL_CONNECTION_ERROR
     except URLError:
@@ -117,11 +68,10 @@ def report_dictionary_validation_status(request):
         return "Unexpected processing error: " + str(e)
     finally:
         delete_file_if_it_exists(input_arguments.get(common_constants.JSON_PATH, ''))
-    return ""
 
 
 def validate_dictionary(input_arguments, hed_schema=None, return_response=True):
-    """Validates the dictionary and returns a
+    """Validates the dictionary and returns a response or a printable string depending on return_response value
 
     Parameters
     ----------
@@ -140,11 +90,11 @@ def validate_dictionary(input_arguments, hed_schema=None, return_response=True):
 
     json_dictionary = ColumnDefGroup(input_arguments.get(common_constants.JSON_PATH, ''))
     if not hed_schema:
-        hed_schema = HedSchema(input_arguments.get(common_constants.HED_XML_FILE, ''))
+        hed_schema = load_schema(input_arguments.get(common_constants.HED_XML_FILE, ''))
     issues = json_dictionary.validate_entries(hed_schema)
     if issues:
         display_name = input_arguments.get(common_constants.JSON_FILE, '')
-        issue_str = get_printable_issue_string(issues, display_name, 'HED validation errors')
+        issue_str = get_printable_issue_string(issues, f"HED validation errors for {display_name}")
         if not return_response:
             return issue_str
         file_name = generate_filename(display_name, suffix='validation_errors', extension='.txt')
diff --git a/hedweb/hed/web/events.py b/hedweb/hed/web/events.py
index a4c07d57f..a89ee42c7 100644
--- a/hedweb/hed/web/events.py
+++ b/hedweb/hed/web/events.py
@@ -4,7 +4,7 @@
 
 from hed.util.file_util import delete_file_if_it_exists
 from hed.validator.hed_validator import HedValidator
-from hed.util.hed_schema import HedSchema
+from hed.schema import hed_schema_file
 
 from hed.web.constants import common_constants, error_constants, file_constants
 from hed.web.dictionary import validate_dictionary
@@ -72,7 +72,7 @@ def report_events_validation_status(request):
     input_arguments = []
     try:
         input_arguments = generate_input_from_events_form(request)
-        hed_schema = HedSchema(input_arguments.get(common_constants.HED_XML_FILE, ''))
+        hed_schema = hed_schema_file.load_schema(input_arguments.get(common_constants.HED_XML_FILE, ''))
         download_response = validate_dictionary(input_arguments, hed_schema=hed_schema)
         if download_response:
             return download_response
diff --git a/hedweb/hed/web/routes.py b/hedweb/hed/web/routes.py
index 9ca98cc6c..3c310b783 100644
--- a/hedweb/hed/web/routes.py
+++ b/hedweb/hed/web/routes.py
@@ -4,15 +4,15 @@
 import traceback
 
 from hed.util import hed_cache
-
-from hed.web import events, spreadsheet, schema, services, utils
-from hed.web.constants import blueprint_constants, common_constants, error_constants, page_constants, route_constants
-from hed.web.web_utils import delete_file_if_it_exists, find_hed_version_in_uploaded_file, save_file_to_upload_folder, \
+from hed.schema import hed_schema_file
+from hed.web import events, spreadsheet, schema, services, spreadsheet_utils
+from hed.web.constants import common_constants, error_constants, page_constants, route_constants
+from hed.web.web_utils import delete_file_if_it_exists, save_file_to_upload_folder, \
     generate_download_file_response, handle_http_error
 from hed.web.dictionary import report_dictionary_validation_status
 
 app_config = current_app.config
-route_blueprint = Blueprint(blueprint_constants.ROUTE_BLUEPRINT, __name__)
+route_blueprint = Blueprint(route_constants.ROUTE_BLUEPRINT, __name__)
 
 
 @route_blueprint.route(route_constants.DELETE_FILE_ROUTE, strict_slashes=False, methods=['GET'])
@@ -59,28 +59,18 @@ def download_file_in_upload_directory(filename, header=None):
 
 @route_blueprint.route(route_constants.DICTIONARY_VALIDATION_SUBMIT_ROUTE, strict_slashes=False, methods=['POST'])
 def get_dictionary_validation_results():
-    """Validate the JSON dictionary in the form after submission and return an attachment other containing the output.
+    """Validate the JSON dictionary in the form after submission and return an attachment containing the output.
 
     Parameters
     ----------
 
     Returns
     -------
-        string
-        A serialized JSON string containing information related to the worksheet columns. If the validation fails then a
-        500 error message is returned.
+        download file
+        A text file with the validation errors.
     """
     validation_response = report_dictionary_validation_status(request)
     return validation_response
-    # validation_response = dictionary.report_dictionary_validation_status(request)
-    # # Success
-    # if isinstance(validation_response, Response):
-    #     return validation_response
-    # if isinstance(validation_response, str):
-    #     if validation_response:
-    #         return handle_http_error(error_constants.INTERNAL_SERVER_ERROR, validation_response, as_text=True)
-    #     else:
-    #         return ""
 
 
 @route_blueprint.route(route_constants.EVENTS_VALIDATION_SUBMIT_ROUTE, strict_slashes=False, methods=['POST'])
@@ -141,9 +131,13 @@ def get_hed_version():
         A serialized JSON string containing information related to the spreadsheet columns.
 
     """
-    hed_info = find_hed_version_in_uploaded_file(request)
-    if error_constants.ERROR_KEY in hed_info:
-        return handle_http_error(error_constants.INTERNAL_SERVER_ERROR, hed_info[error_constants.ERROR_KEY])
+    hed_info = {}
+    try:
+        if common_constants.HED_XML_FILE in request.files:
+            hed_file_path = save_file_to_upload_folder(request.files[common_constants.HED_XML_FILE])
+            hed_info[common_constants.HED_VERSION] = hed_schema_file.get_hed_xml_version(hed_file_path)
+    except:
+        return handle_http_error(error_constants.INTERNAL_SERVER_ERROR, traceback.format_exc())
     return json.dumps(hed_info)
 
 
@@ -238,7 +232,7 @@ def get_spreadsheet_columns_info():
         A serialized JSON string containing information related to the spreadsheet columns.
 
     """
-    spreadsheet_columns_info = utils.find_spreadsheet_columns_info(request)
+    spreadsheet_columns_info = spreadsheet_utils.find_spreadsheet_columns_info(request)
     if error_constants.ERROR_KEY in spreadsheet_columns_info:
         return handle_http_error(error_constants.INTERNAL_SERVER_ERROR,
                                  spreadsheet_columns_info[error_constants.ERROR_KEY])
@@ -293,7 +287,7 @@ def get_worksheets_info():
             workbook_file = request.files[common_constants.SPREADSHEET_FILE]
             workbook_file_path = save_file_to_upload_folder(workbook_file)
             if workbook_file_path:
-                worksheets_info = utils.populate_worksheets_info_dictionary(worksheets_info, workbook_file_path)
+                worksheets_info = spreadsheet_utils.populate_worksheets_info_dictionary(worksheets_info, workbook_file_path)
     except:
         worksheets_info[error_constants.ERROR_KEY] = traceback.format_exc()
     finally:
diff --git a/hedweb/hed/web/schema.py b/hedweb/hed/web/schema.py
index 4930361e6..16f27c6bd 100644
--- a/hedweb/hed/web/schema.py
+++ b/hedweb/hed/web/schema.py
@@ -4,12 +4,13 @@
 from urllib.parse import urlparse
 from flask import current_app, Response
 
-from hed.schema import xml2wiki, wiki2xml, schema_validator
+from hed.schema.hed_schema_file import load_schema, convert_schema_to_format
+from hed.util.error_reporter import get_printable_issue_string
 from hed.util.file_util import delete_file_if_it_exists, url_to_file, get_file_extension
 from hed.util.exceptions import HedFileError
 
-from hed.web.web_utils import file_extension_is_valid, form_has_file, form_has_option, form_has_url, \
-    generate_download_file_response, generate_filename, get_printable_issue_string, \
+from hed.web.web_utils import form_has_file, form_has_option, form_has_url, \
+    generate_download_file_response, generate_filename,  \
     handle_http_error, save_file_to_upload_folder, save_text_to_upload_folder
 
 from hed.web.constants import common_constants, error_constants, file_constants
@@ -48,31 +49,6 @@ def generate_input_from_schema_form(request):
     return arguments
 
 
-def get_schema_conversion(schema_local_path):
-    """Runs the appropriate xml<>mediawiki converter depending on input filetype.
-
-    returns: A dictionary with converter.constants filled in.
-    """
-
-    try:
-        if not schema_local_path:
-            raise ValueError(f"Invalid input schema path")
-        input_extension = get_file_extension(schema_local_path)
-
-        if input_extension == file_constants.SCHEMA_XML_EXTENSION:
-            conversion_function = xml2wiki.convert_hed_xml_2_wiki
-        elif input_extension == file_constants.SCHEMA_WIKI_EXTENSION:
-            conversion_function = wiki2xml.convert_hed_wiki_2_xml
-        else:
-            raise ValueError(f"Invalid extension type: {input_extension}")
-        converted_schema_path, errors = conversion_function(None, schema_local_path, check_for_issues=False)
-    except ValueError as error:
-        errors = format(error)
-        converted_schema_path = ''
-
-    return converted_schema_path, errors
-
-
 def run_schema_compliance_check(request):
     """Run tag comparison(map_schema from converter)
 
@@ -85,20 +61,12 @@ def run_schema_compliance_check(request):
     try:
         input_arguments = generate_input_from_schema_form(request)
         hed_file_path = input_arguments.get(common_constants.SCHEMA_PATH, '')
-        if hed_file_path and hed_file_path.endswith(".mediawiki"):
-            new_file_path, errors = get_schema_conversion(hed_file_path)
-            if new_file_path:
-                delete_file_if_it_exists(hed_file_path)
-                hed_file_path = new_file_path
-
-        if not hed_file_path or not file_extension_is_valid(hed_file_path, [file_constants.SCHEMA_XML_EXTENSION]):
-            return f"Invalid file name {hed_file_path}"
-
-        issues = schema_validator.validate_schema(hed_file_path)
+        this_schema = load_schema(hed_file_path)
+        issues = this_schema.check_compliance()
         if issues:
             display_name = input_arguments.get(common_constants.SCHEMA_DISPLAY_NAME)
-            issue_str = get_printable_issue_string(issues, display_name, 'Schema HED 3G compliance errors for ')
-            file_name = generate_filename(display_name, suffix='schema_errors', extension='.txt')
+            issue_str = get_printable_issue_string(issues, f"Schema HED 3G compliance errors for {display_name}")
+            file_name = generate_filename(display_name, suffix='schema_3G_compliance_errors', extension='.txt')
             issue_file = save_text_to_upload_folder(issue_str, file_name)
             download_response = generate_download_file_response(issue_file, display_name=file_name, category='warning',
                                                                 msg='Schema is not HED 3G compliant')
@@ -125,14 +93,17 @@ def run_schema_conversion(request):
         Non empty string is an error
         Response is a download success.
     """
-    hed_file_path = ''
+    schema_file_path = ''
     try:
         input_arguments = generate_input_from_schema_form(request)
-        hed_file_path = input_arguments.get(common_constants.SCHEMA_PATH)
+        schema_file_path = input_arguments.get(common_constants.SCHEMA_PATH)
         display_name = input_arguments.get(common_constants.SCHEMA_DISPLAY_NAME)
-        schema_file, issues = get_schema_conversion(hed_file_path)
+        input_extension = get_file_extension(schema_file_path)
+        save_wiki = input_extension == file_constants.SCHEMA_XML_EXTENSION
+        schema_file, issues = convert_schema_to_format(local_hed_file=schema_file_path, check_for_issues=False,
+                                                       display_filename=display_name, save_as_mediawiki=save_wiki)
         if issues:
-            issue_str = get_printable_issue_string(issues, display_name, 'Schema conversion errors for ')
+            issue_str = get_printable_issue_string(issues, f"Schema conversion errors for {display_name}")
             file_name = generate_filename(display_name, suffix='conversion_errors', extension='.txt')
             issue_file = save_text_to_upload_folder(issue_str, file_name)
             download_response = \
@@ -158,4 +129,4 @@ def run_schema_conversion(request):
     except:
         return traceback.format_exc()
     finally:
-        delete_file_if_it_exists(hed_file_path)
+        delete_file_if_it_exists(schema_file_path)
diff --git a/hedweb/hed/web/services.py b/hedweb/hed/web/services.py
index 4641918bf..5dd92f016 100644
--- a/hedweb/hed/web/services.py
+++ b/hedweb/hed/web/services.py
@@ -1,51 +1,19 @@
+import os
 import json
 import traceback
-from urllib.error import URLError, HTTPError
 from flask import current_app
 
-from hed.util.file_util import get_file_extension, delete_file_if_it_exists
+from hed.schema import hed_schema_file
+from hed.util import hed_cache
+from hed.util.file_util import delete_file_if_it_exists
 from hed.validator.hed_validator import HedValidator
-from hed.util.hed_file_input import HedFileInput
+from hed.util.column_def_group import ColumnDefGroup
+from hed.util.error_types import ErrorContext
 
-from hed.web.constants import common_constants, error_constants, file_constants
-from hed.web.web_utils import convert_number_str_to_list, generate_filename, get_printable_issue_string, \
-    generate_download_file_response, get_hed_path_from_pull_down, get_uploaded_file_path_from_form, \
-    handle_http_error, save_file_to_upload_folder, save_text_to_upload_folder
-from hed.web import utils
 
 app_config = current_app.config
 
 
-def generate_arguments_from_form(request):
-    """Gets the input arguments from a request object associated with a service.
-
-    Parameters
-    ----------
-    request: Request object
-        A Request object containing user data from the validation form.
-
-    Returns
-    -------
-    dictionary
-        A dictionary containing input arguments for calling the underlying validation function.
-    """
-    hed_file_path, hed_display_name = get_hed_path_from_pull_down(request)
-    uploaded_file_name, original_file_name = \
-        get_uploaded_file_path_from_form(request, common_constants.SPREADSHEET_FILE,
-                                         file_constants.SPREADSHEET_FILE_EXTENSIONS)
-
-    input_arguments = { }
-    #     common_constants.HED_VERSION:  form_data = request.form,
-    #     common_constants.HED_DISPLAY_NAME: hed_display_name,
-    #
-    #
-    #     common_constants.CHECK_FOR_WARNINGS:
-    #         utils.get_optional_form_field(request, common_constants.CHECK_FOR_WARNINGS,
-    #                                       common_constants.BOOLEAN)
-    # }
-    return input_arguments
-
-
 def report_services_status(request):
     """
     Reports validation status of hed strings associated with EEG events received from EEGLAB plugin HEDTools
@@ -62,31 +30,108 @@ def report_services_status(request):
         A serialized JSON string containing information related to the hed strings validation result.
         If the validation fails then a 500 error message is returned.
     """
-    validation_status = {}
-
-    # Parse uploaded data
-    form_data = request.form
-    check_for_warnings = form_data["check_for_warnings"] == '1' if "check_for_warnings" in form_data else False
-    # if hed_xml_file was submitted, it's accessed by request.files, otherwise empty
-    if "hed-xml-file" in request.files and get_file_extension(request.files["hed-xml-file"].filename) == "xml":
-        hed_xml_file = save_file_to_upload_folder(request.files["hed-xml-file"])
-    else:
-        hed_xml_file = ''
-
+    response = {"result": "", "errors": "", "error_details": ""}
+    hed_xml_file = ''
     try:
-        # parse hed_strings from json
-        hed_strings = json.loads(form_data["hed-strings"])
-        # hed_strings is a list of HED strings associated with events in EEG.event (order preserved)
-        hed_input_reader = HedValidator(hed_strings, check_for_warnings=check_for_warnings, hed_xml_file=hed_xml_file)
-        # issues is a list of lists. Element list is empty if no error,
-        # else is a list of dictionaries, each dictionary contains an error-message key-value pair
-        issues = hed_input_reader.get_validation_issues()
-
-        # Prepare response
-        validation_status["issues"] = issues
+        form_data = request.data
+        form_string = form_data.decode()
+        param_dict = json.loads(form_string)
+        service = param_dict.get("service", "")
+        supported_services = get_services()
+        if not service:
+            response["errors"] = "Must specify a service"
+        elif service not in supported_services.keys():
+            response["errors"] = f"{service} not supported"
+        elif service == "get_services":
+            response["result"] = {"supported_services": supported_services}
+        elif service == "validate_json":
+            response["result"] = get_validate_dictionary(param_dict)
+        elif service == "validate_strings":
+            response["result"] = get_validate_strings(param_dict)
+        else:
+            response["errors"] = f"{service} not implemented"
     except:
-        validation_status[error_constants.ERROR_KEY] = traceback.format_exc()
+        response["error_details"] = traceback.format_exc()
+        response["errors"] = "An exception thrown during execution of service"
     finally:
         delete_file_if_it_exists(hed_xml_file)
 
-    return validation_status
+    return response
+
+
+def get_services():
+    dir_path = os.path.dirname(os.path.realpath(__file__))
+    the_path = os.path.join(dir_path, 'static/resources/services.json')
+    with open(the_path) as f:
+        service_info = json.load(f)
+    return service_info
+
+
+def get_validate_dictionary(params):
+    """
+    Reports validation status of hed strings
+
+    Parameters
+    ----------
+    params: dict
+         A Request object containing user data submitted by HEDTools.
+         Keys include "hed_strings", "check_for_warnings", and "hed_xml_file"
+
+    Returns
+    -------
+    dict
+         A serialized JSON string containing information related to the hed strings validation result.
+    """
+    hed = params.get("hed_version", "")
+    hed_file_path = hed_cache.get_path_from_hed_version(hed)
+    hed_schema = hed_schema_file.load_schema(hed_file_path)
+    json_text = params.get("json_dictionary", "")
+    json_dictionary = ColumnDefGroup(json_string=json_text)
+    issues = json_dictionary.validate_entries(hed_schema)
+    issue_list = []
+    for issue in issues:
+        code = issue.get('code', "")
+        message = issue.get('message', "")
+        column_name = issue.get(ErrorContext.SIDECAR_COLUMN_NAME, "")
+        if column_name:
+            column_name = column_name[0]
+        key_name = issue.get(ErrorContext.SIDECAR_KEY_NAME, "")
+        if key_name:
+            key_name = key_name[0]
+        hed_string = issue.get(ErrorContext.SIDECAR_HED_STRING, "")
+        if hed_string:
+            hed_string = hed_string[0]
+        issue_dict = {"code": code, "message": message, "column_name": column_name,
+                      "key_name": key_name, "hed_string": hed_string}
+        issue_list.append(issue_dict)
+    result = {'hed_version': hed, 'validation_errors': issue_list}
+    return result
+
+
+def get_validate_strings(params):
+    """
+    Reports validation status of hed strings
+
+    Parameters
+    ----------
+    params: dict
+         A Request object containing user data submitted by HEDTools.
+         Keys include "hed_strings", "check_for_warnings", and "hed_xml_file"
+
+    Returns
+    -------
+    list of dict
+         A serialized JSON string containing information related to the hed strings validation result.
+    """
+    hed = params.get("hed_version", "")
+    hed_file_path = hed_cache.get_path_from_hed_version(hed)
+    hed_strings = params.get("hed_strings", "")
+    hed_validator = HedValidator(hed_xml_file=hed_file_path)
+    validation_issues = {}
+    str_num = 1
+    for hed_string in hed_strings:
+        issue = hed_validator.validate_input(hed_string)
+        if issue:
+            validation_issues[f"String_{str_num}"] = issue
+        str_num += 1
+    return {'hed_version': hed, 'validation_errors': validation_issues}
diff --git a/hedweb/hed/web/spreadsheet.py b/hedweb/hed/web/spreadsheet.py
index 2f50f7173..f607cba50 100644
--- a/hedweb/hed/web/spreadsheet.py
+++ b/hedweb/hed/web/spreadsheet.py
@@ -1,16 +1,17 @@
 from urllib.error import URLError, HTTPError
 from flask import current_app
 
+from hed.util.error_reporter import get_printable_issue_string
 from hed.util.file_util import delete_file_if_it_exists
 from hed.validator.hed_validator import HedValidator
 from hed.util.hed_file_input import HedFileInput
 
 import hed.web.web_utils
 from hed.web.constants import common_constants, error_constants, file_constants
-from hed.web.web_utils import convert_number_str_to_list, generate_filename, get_printable_issue_string, \
+from hed.web.web_utils import convert_number_str_to_list, generate_filename, \
     generate_download_file_response, get_hed_path_from_pull_down, get_uploaded_file_path_from_form, \
     handle_http_error, save_text_to_upload_folder
-from hed.web import utils
+from hed.web import spreadsheet_utils
 
 app_config = current_app.config
 
@@ -33,21 +34,24 @@ def generate_input_from_validation_form(request):
         get_uploaded_file_path_from_form(request, common_constants.SPREADSHEET_FILE,
                                          file_constants.SPREADSHEET_FILE_EXTENSIONS)
 
-    validation_input_arguments = {
+    input_arguments = {
         common_constants.HED_XML_FILE: hed_file_path,
         common_constants.HED_DISPLAY_NAME: hed_display_name,
         common_constants.SPREADSHEET_PATH: uploaded_file_name,
         common_constants.SPREADSHEET_FILE: original_file_name,
         common_constants.TAG_COLUMNS: convert_number_str_to_list(request.form[common_constants.TAG_COLUMNS]),
-        common_constants.COLUMN_PREFIX_DICTIONARY: utils.get_specific_tag_columns_from_form(request),
-        common_constants.WORKSHEET_NAME: hed.web.web_utils.get_optional_form_field(request, common_constants.WORKSHEET_NAME,
+        common_constants.COLUMN_PREFIX_DICTIONARY: spreadsheet_utils.get_specific_tag_columns_from_form(request),
+        common_constants.WORKSHEET_NAME: hed.web.web_utils.get_optional_form_field(request,
+                                                                                   common_constants.WORKSHEET_NAME,
                                                                                    common_constants.STRING),
         common_constants.HAS_COLUMN_NAMES:
-            hed.web.web_utils.get_optional_form_field(request, common_constants.HAS_COLUMN_NAMES, common_constants.BOOLEAN),
+            hed.web.web_utils.get_optional_form_field(request, common_constants.HAS_COLUMN_NAMES,
+                                                      common_constants.BOOLEAN),
         common_constants.CHECK_FOR_WARNINGS:
-            hed.web.web_utils.get_optional_form_field(request, common_constants.CHECK_FOR_WARNINGS, common_constants.BOOLEAN)
+            hed.web.web_utils.get_optional_form_field(request, common_constants.CHECK_FOR_WARNINGS,
+                                                      common_constants.BOOLEAN)
     }
-    return validation_input_arguments
+    return input_arguments
 
 
 def report_spreadsheet_validation_status(request):
@@ -115,7 +119,7 @@ def validate_spreadsheet(input_arguments, hed_validator=None, return_response=Tr
         if worksheet_name:
             title_string = display_name + ' [worksheet ' + worksheet_name + ']'
             suffix = '_worksheet_' + worksheet_name + '_' + suffix
-        issue_str = get_printable_issue_string(issues, title_string, 'HED validation errors')
+        issue_str = get_printable_issue_string(issues, f"{title_string} HED validation errors")
         if not return_response:
             return issue_str
         file_name = generate_filename(display_name, suffix=suffix, extension='.txt')
diff --git a/hedweb/hed/web/utils.py b/hedweb/hed/web/spreadsheet_utils.py
similarity index 95%
rename from hedweb/hed/web/utils.py
rename to hedweb/hed/web/spreadsheet_utils.py
index 7470cef53..0248543ff 100644
--- a/hedweb/hed/web/utils.py
+++ b/hedweb/hed/web/spreadsheet_utils.py
@@ -1,10 +1,7 @@
 import xlrd
 import traceback
-
 from flask import current_app
-
 from hed.util.file_util import get_file_extension, delete_file_if_it_exists
-
 from hed.web.constants import common_constants, error_constants, spreadsheet_constants
 from hed.web.web_utils import save_file_to_upload_folder, find_all_str_indices_in_list
 
@@ -31,7 +28,7 @@ def find_spreadsheet_columns_info(request):
         if common_constants.SPREADSHEET_FILE in request.files:
             spreadsheet_file = request.files[common_constants.SPREADSHEET_FILE]
             spreadsheet_file_path = save_file_to_upload_folder(spreadsheet_file)
-            if spreadsheet_file_path and worksheet_name_present_in_form(request):
+            if spreadsheet_file_path and common_constants.WORKSHEET_NAME in request.form:
                 worksheet_name = request.form[common_constants.WORKSHEET_NAME]
                 spreadsheet_columns_info = populate_spreadsheet_columns_info_dictionary(spreadsheet_columns_info,
                                                                                         spreadsheet_file_path,
@@ -271,20 +268,3 @@ def populate_worksheets_info_dictionary(worksheets_info, spreadsheet_file_path):
     worksheets_info[common_constants.REQUIRED_TAG_COLUMN_INDICES] = \
         get_spreadsheet_specific_tag_column_indices(worksheets_info[common_constants.COLUMN_NAMES])
     return worksheets_info
-
-
-def worksheet_name_present_in_form(request):
-    """Checks to see if a worksheet name is present in a request object from form.
-
-    Parameters
-    ----------
-    request: Request object
-        A Request object containing user data from a form.
-
-    Returns
-    -------
-    boolean
-        True if a worksheet name is present in a request object from the form.
-
-    """
-    return common_constants.WORKSHEET_NAME in request.form
diff --git a/hedweb/hed/web/static/resources/services.json b/hedweb/hed/web/static/resources/services.json
new file mode 100644
index 000000000..df0d31c60
--- /dev/null
+++ b/hedweb/hed/web/static/resources/services.json
@@ -0,0 +1,46 @@
+{
+  "get_services": {
+    "Name": "get_services",
+    "Description": "Return a list of hed services commands with descriptions",
+    "Parameters" : [],
+    "Returns" : [
+      {"Name": "supported_services",
+       "Description": "A list of available services with description, parameters and return values."}]
+  },
+  "validate_strings": {
+     "Name": "validate_strings",
+     "Description": "Validate a list of HED strings and return errors",
+     "Parameters": [
+         {"Name": "hed_version",
+          "Description": "Version of HED to be used to validate",
+          "Default": "Latest"},
+         {"Name": "hed_strings",
+          "Description": "List of hed strings to be validated",
+          "Default": []}
+       ],
+     "Returns": [
+         {"Name": "hed_version",
+          "Description": "Version of HED used to validate"},
+         {"Name": "validation_errors",
+          "Description": "List of validation error dictionaries"}
+     ]
+  },
+    "validate_json": {
+     "Name": "validate_json",
+     "Description": "Validate a JSON string in form of BIDS sidecar and return errors",
+     "Parameters": [
+         {"Name": "hed_version",
+          "Description": "Version of HED to be used to validate",
+          "Default": "Latest"},
+         {"Name": "json_dictionary",
+          "Description": "JSON sidecar as a string",
+          "Default": []}
+       ],
+     "Returns": [
+         {"Name": "hed_version",
+          "Description": "Version of HED used to validate"},
+         {"Name": "validation_errors",
+          "Description": "List of validation error dictionaries"}
+     ]
+  }
+}
\ No newline at end of file
diff --git a/hedweb/hed/web/templates/hed-services.html b/hedweb/hed/web/templates/hed-services.html
index f85acafb0..ba80d4823 100644
--- a/hedweb/hed/web/templates/hed-services.html
+++ b/hedweb/hed/web/templates/hed-services.html
@@ -5,6 +5,7 @@
     <title>HED services</title>
 </head>
 <body>
+<h1>Hi</h1>
 <form id="hed-services-form" method="POST" enctype="multipart/form-data">
     <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
 </form>
diff --git a/hedweb/hed/web/web_utils.py b/hedweb/hed/web/web_utils.py
index 0990c30d1..707966461 100644
--- a/hedweb/hed/web/web_utils.py
+++ b/hedweb/hed/web/web_utils.py
@@ -7,10 +7,8 @@
 from flask import current_app, jsonify, Response
 
 from hed.util import hed_cache
-from hed.util.error_reporter import ErrorHandler
 from hed.util.file_util import get_file_extension, delete_file_if_it_exists
-from hed.util.hed_schema import HedSchema
-from hed.web.constants import common_constants, error_constants
+from hed.web.constants import common_constants
 
 app_config = current_app.config
 
@@ -76,32 +74,6 @@ def find_all_str_indices_in_list(list_of_str, str_value):
             value.lower().replace(' ', '') == str_value.lower().replace(' ', '')]
 
 
-def find_hed_version_in_uploaded_file(request, key_name=common_constants.HED_XML_FILE):
-    """Finds the version number in an HED XML other.
-
-    Parameters
-    ----------
-    request: Request object
-        A Request object containing user data
-    key_name: str
-        Name of the key for the HED XML file in the request
-
-    Returns
-    -------
-    string
-        A serialized JSON string containing the version number information.
-
-    """
-    hed_info = {}
-    try:
-        if key_name in request.files:
-            hed_file_path = save_file_to_upload_folder(request.files[key_name])
-            hed_info[common_constants.HED_VERSION] = HedSchema.get_hed_xml_version(hed_file_path)
-    except:
-        hed_info[error_constants.ERROR_KEY] = traceback.format_exc()
-    return hed_info
-
-
 def form_has_file(request, file_field, valid_extensions):
     """Checks to see if a file name with valid extension is present in the request object.
 
@@ -195,7 +167,6 @@ def generate_download_file_response(download_file, display_name=None, header=Non
     """
     if not display_name:
         display_name = download_file
-    print("In generate:", display_name)
     try:
         if not download_file:
             return f"No download file given"
@@ -280,36 +251,6 @@ def get_hed_path_from_pull_down(request):
     return hed_file_path, hed_display_name
 
 
-def get_printable_issue_string(issues, display_name=None, title_string=''):
-    """Returns a string suitable for text blob download
-
-     Parameters
-     ----------
-     issues: list
-         List of the issues to be printed.
-     title_string: str
-         key name in the files dictionary of the Request object
-     display_name: list of str
-         List of valid extensions
-
-     Returns
-     -------
-     tuple
-         A tuple containing the other paths. The two other paths are for the spreadsheet and a optional HED XML other.
-     """
-    if not issues:
-        return ''
-    if title_string:
-        title = title_string
-    else:
-        title = ''
-    if display_name:
-        title = display_name + ' ' + title_string + '\n'
-    if not title:
-        title = None
-    return ErrorHandler.get_printable_issue_string(issues, title, skip_filename=True)
-
-
 def get_optional_form_field(request, form_field_name, type=''):
     """Gets the specified optional form field if present.
 
@@ -417,31 +358,6 @@ def save_file_to_upload_folder(file_object, delete_on_close=False):
     return file_path
 
 
-def save_issues_to_upload_folder(issues, filename):
-    """Saves the issues found the upload folder as filename.
-
-    Parameters
-    ----------
-    issues: string
-        A string containing the validation issues.
-    filename: str
-        File name of the issue folder
-
-    Returns
-    -------
-    string
-        The name of the validation output other.
-
-    """
-
-    issues_file_path = os.path.join(current_app.config['UPLOAD_FOLDER'], filename)
-    with open(issues_file_path, 'w', encoding='utf-8') as issues_file:
-        for val_issue in issues:
-            issues_file.write(val_issue['message'] + "\n")
-    issues_file.close()
-    return issues_file_path
-
-
 def save_text_to_upload_folder(text, filename):
     """Saves a string in the upload folder as filename.
 


Test patch:
diff --git a/hedtools/tests/data/HED8.0.0-alpha.1.xml b/hedtools/tests/data/HED8.0.0-alpha.1.xml
index c368a6d59..23b68b41e 100644
--- a/hedtools/tests/data/HED8.0.0-alpha.1.xml
+++ b/hedtools/tests/data/HED8.0.0-alpha.1.xml
@@ -1,6 +1,8 @@
 <?xml version="1.0" ?>
 <HED version="8.0.0-alpha.1">
    <node>
+      <prologue>This is a prologue.
+      This is a second line of a prologue.</prologue>
       <name>Event</name>
       <node>
          <name>Sensory-event</name>
@@ -3392,4 +3394,6 @@
          <description>SI unit submultiple representing 10^-24</description>
       </unitModifier>
    </unitModifiers>
+   <epilogue>This is an epilogue.
+      This is a second line of an epilogue.</epilogue>
 </HED>
diff --git a/hedtools/tests/data/invalid_wiki_schemas/HED_default.mediawiki b/hedtools/tests/data/invalid_wiki_schemas/HED_default.mediawiki
new file mode 100644
index 000000000..fb8021e37
--- /dev/null
+++ b/hedtools/tests/data/invalid_wiki_schemas/HED_default.mediawiki
@@ -0,0 +1,1104 @@
+HED version:8.0.0-alpha.1
+
+
+This is a prologue line.
+This is a second prologue line.
+
+!# start schema
+
+'''Event'''
+* Sensory-event <nowiki>[Something perceivable by the participant. An event meant to be an experimental stimulus should also be tagged with Attribute/Contextual/Task-related. Other events may be tagged with Attribute/Contextual/Non task-related.]</nowiki>
+* Agent-action <nowiki>[Any action engaged in by an agent. An event which is a participant response to an experimental stimulus should also be tagged with Attribute/Task-related and Attribute/Role/Experimental subject.]</nowiki>
+* Data-feature <nowiki>[An event derived from the data itself, possibly inserted in a post-hoc fashion, for example an interictal spike or alpha burst.]</nowiki>
+* Experiment-control <nowiki>{extensionAllowed} [An event pertaining to the control of the experiment during its operation.]</nowiki>
+* Experiment-procedure <nowiki>[An event indicating an experimental procedure as in doing a saliva swab on the person during the experiment or administering a survey.]</nowiki>
+* Experiment-structure <nowiki>{extensionAllowed} [An event specifying the structure of the experiment.]</nowiki>
+
+'''Agent''' <nowiki>{extensionAllowed}</nowiki>
+* Animal-agent
+* Avatar-agent
+* Computational-agent
+* Controller-agent <nowiki>[An outside controller such as the experimental control software.]</nowiki>
+* Human-agent
+* Robotic-agent
+
+'''Action''' <nowiki>{extensionAllowed} [May or may not be in response to a previous Sensory presentation event.]</nowiki>
+* Move
+** Breathe
+*** Inhale <nowiki>[A deliberate inbreath, e.g. in response to a task instruction. If incidental, group it with Attribute/Cognitive modifier/Incidental. E.g. (Action/Move/Breathe/Inhale, Attribute/Cognitive modifier/Incidental).]</nowiki>
+*** Exhale <nowiki>[A deliberate outbreath, e.g. in response to a task instruction. If incidental, group it with Attribute/Cognitive modifier/Incidental. E.g. (Action/Move/Breathe/Exhale, Attribute/Cognitive modifier/Incidental).]</nowiki>
+*** Hold-breath
+** Move-full-body
+*** Bend
+*** Dance
+*** Fall-down <nowiki>[Involuntary fall to floor or ground]</nowiki>
+*** Flex
+*** Jerk <nowiki>[When some force applied to the participant causes them to involuntarily accommodate or react to it.]</nowiki>
+*** Recover-balance <nowiki>[A movement made in response to a stumble or unforeseen loss of balance]</nowiki>
+*** Stretch <nowiki>[Stretching the body or body part]</nowiki>
+*** Shudder
+*** Stumble <nowiki>[Temporary and involuntary loss of balance]</nowiki>
+*** Turn
+** Move-head
+*** Move-eyes
+**** Blink
+**** Close-eyes
+**** Fixate
+**** Inhibit-blinks
+**** Open-eyes
+**** Saccade
+*** Move-mouth
+**** Burp
+**** Clear-throat
+**** Cough
+**** Gurgle
+**** Hiccup
+**** Smile
+**** Sneeze
+**** Sniffle
+**** Swallow
+**** Yawn
+*** Nod
+*** Shake-head
+** Move-arm-hand
+*** Clap
+*** Drop <nowiki>[For example the participant drops a handheld ball]</nowiki>
+*** Grab
+*** Grasp <nowiki>[Grasp an object in one or both hands]</nowiki>
+*** Hold-down
+*** Lift <nowiki>[Often grouped with item being lifted.]</nowiki>
+*** Make-fist
+*** Point <nowiki>[Should be grouped with Item/Agent/Human/Body part/Hand/Finger tag to specify which finger was used for the action.]</nowiki>
+*** Press <nowiki>[Often grouped with body part doing action and item being pressed.]</nowiki>
+*** Push
+*** Reach <nowiki>[Requires a spatial goal such as reaching to touch a button or to grasp something. A body stretch is not a reach.]</nowiki>
+*** Release <nowiki>[Often grouped with item being released.]</nowiki>
+*** Retract <nowiki>[Often grouped with body part doing action and item being retracted.]</nowiki>
+*** Scratch
+*** Snap-fingers
+*** Tap <nowiki>[Often grouped with body part doing action.]</nowiki>
+*** Touch <nowiki>[May follow a Reach event]</nowiki>
+** Move-leg-foot
+*** Curl-toes
+*** Jog
+*** Jump
+*** Kick
+*** Pedal
+*** Run
+*** Step
+**** Heel-strike
+**** Toe-off
+*** Trot
+*** Walk
+* Communicate
+** Communicate-vocally
+*** Cry
+*** Groan
+*** Gulp <nowiki>[Communicate anxiety. Use swallow if non-editorial.]</nowiki>
+*** Laugh
+*** Scream
+*** Sigh
+*** Speak <nowiki>[Communicate using spoken language.]</nowiki>
+** Gesture <nowiki>[Make a expressive communicative movement of hands/arms and, often, face]</nowiki>
+*** Index-up
+*** Pump-fist
+*** Spread-hand
+*** Shhh
+*** Shrug
+*** Thumb-up
+*** Wave
+** Communicate-musically
+*** Hum
+*** Sing
+*** Vocalize <nowiki>[Make a sustained vowel sound such as, Ahhh]</nowiki>
+*** Whistle
+* Perform <nowiki>[Carry out or accomplish an action or task or function.]</nowiki>
+** Change
+** Collide
+** Fill-out-survey
+** Operate <nowiki>[Control something else.]</nowiki>
+*** Drive <nowiki>[Control something that moves.]</nowiki>
+*** Brake <nowiki>[Apply brakes to stop something.]</nowiki>
+*** Steer
+** Play <nowiki>[As a musical instrument or a game.]</nowiki>
+** Rest
+* Think
+** Allow <nowiki>[Allow access to something such as allowing a car to pass]</nowiki>
+** Attend-to
+** Count
+** Deny <nowiki>[Deny access to something such as preventing someone to pass]</nowiki>
+** Detect
+** Discriminate
+** Encode
+** Evade
+** Generate
+** Identify
+** Imagine
+** Read
+** Recall
+** Repeat
+** Switch-attention
+** Track
+
+'''Item''' <nowiki>{extensionAllowed}</nowiki>
+* Object <nowiki>{extensionAllowed} [Physical object perceptible via Visual, Auditory, Olfactory, and/or Tactile modalities.]</nowiki>
+** Geometric
+*** Pattern
+**** Dots
+**** Led-pattern
+*** 2D-shape
+**** Clockface
+**** Cross
+**** Ellipse
+***** Circle
+**** Rectangle
+***** Square
+**** Single-point
+**** Star
+**** Triangle
+*** 3D-shape
+**** Box
+***** Cube
+**** Cone
+**** Cylinder
+**** Ellipsoid
+***** Sphere
+**** Pyradmid
+** Man-made-object <nowiki>[A manmade object]</nowiki>
+*** Media
+**** Sound-clip
+**** Visualization <nowiki>[A visual representation of something else -- such as using a stick-figure to represent motion capture data.]</nowiki>
+***** Animation
+***** Art-installation
+***** Braille
+***** Cutout
+***** Image <nowiki>[A still 2-D representation.]</nowiki>
+****** Cartoon
+****** Drawing
+****** Icon
+****** Painting
+****** Photograph
+***** Movie
+***** Outline-visualization
+***** Point-light-visualization
+***** Sculpture
+***** Stick-figure-visualization
+*** Building <nowiki>[Whole building or building feature]</nowiki>
+**** Room
+**** Roof
+**** Entrance
+**** Attic
+**** Basement
+*** Clothing
+*** Device
+**** Computing-device
+**** Engine
+**** IO-device
+***** Input-device
+****** Button
+****** Joystick
+****** Keyboard
+******* Key <nowiki>{takesValue}</nowiki>
+******** <nowiki>#  [Value of key.]</nowiki>
+****** Keypad <nowiki>{takesValue}</nowiki>
+******* <nowiki>#  [Value of keypad key.]</nowiki>
+****** Microphone
+****** Mouse
+******* Mouse-button
+******* Scroll-wheel
+****** Touch-screen
+***** Network-device
+***** Output-device
+****** Display-device
+******* Head-mounted-display
+******* LED-display
+******* Screen
+******** Screen-window
+****** Auditory-device
+******* Headphones
+******* Screen-speaker
+******* Loudspeaker
+***** Recording-device
+****** EEG-recording
+****** File-recording
+****** MEG-recording
+****** Motion-capture
+****** Tape-recording
+**** Machine
+***** Noisemaker
+**** Measurement-device
+***** Clock
+****** Clock-face
+**** Robot
+**** Tool
+*** Document <nowiki>[A physical, written document]</nowiki>
+**** Manuscript
+**** Letter
+**** Note
+**** Book
+**** Notebook
+*** Furnishing <nowiki>[Furniture and appliances and other movable accessories including curtains and rugs]</nowiki>
+*** Navigational-object
+**** Path
+**** Road
+***** Lane
+**** Runway
+**** Sign
+*** Vehicle
+**** Aircraft
+**** Bicycle
+**** Boat
+**** Car
+**** Cart
+**** Tractor
+**** Train
+**** Truck
+** Natural-object
+*** Organism
+**** Animal
+**** Human
+**** Plant
+*** Mineral
+*** Natural-feature
+**** Field
+**** Hill
+**** Mountain
+**** River
+**** Waterfall
+** Food
+** Drink
+* Language <nowiki>[Perceivable in a visual or auditory (spoken) sensory presentation]</nowiki>
+** Character
+** Glyph
+** Nonword
+** Phoneme
+** Phrase
+** Sentence
+** Syllable
+** Textblock
+** Word
+* Sound <nowiki>[Item/Sound can be used in a tag group to describe, for example, sound created by an action Item/Sound, Action/Move/Move leg-foot/Walk, Agent/Human or an object or Item/Sound, Item/Object/Man-made/Vehicle/Car]</nowiki>
+** Environmental-sound <nowiki>[A background sound from the surrounding environment.]</nowiki>
+*** Crowd sound <nowiki>[Multiple speech streams at once]</nowiki>
+*** Noise
+**** White-noise
+**** Colored-noise
+** Musical-sound <nowiki>[Any tone or combination of tones with characteristics such as controlled pitch and timbre that can be controlled by an agent.]</nowiki>
+*** Tone
+*** Instrument-sound
+*** Vocalized-sound
+** Named-animal-sound
+*** Bark
+*** Bleat
+*** Crow
+*** Chirp
+*** Growl
+*** Meow
+*** Moo
+*** Purr
+*** Roar
+** Named-object-sound
+*** Alarm
+*** Buzz
+*** Ka-ching <nowiki>[Cash register sound]</nowiki>
+*** Click
+*** Ding
+*** Horn
+*** Siren
+
+'''Agent-property'''
+* Cognitive-state <nowiki>{requireChild, extensionAllowed}</nowiki>
+** Alert
+** Anesthetized
+** Asleep
+** Attentive
+** Awake
+** Brain-dead
+** Comatose
+** Drowsy
+** Intoxicated <nowiki>[Indicates a state in which participant has consumed alcohol or drugs or other substances that may alter physical or mental state.]</nowiki>
+** Locked-in
+** Passive
+** Resting
+** Vegetative
+* Emotional-state <nowiki>{requireChild, extensionAllowed}</nowiki>
+** Angry
+** Aroused
+** Awed
+** Compassionate
+** Content
+** Disgusted
+** Emotionally-neutral
+** Empathetic
+** Excited
+** Fearful
+** Feeling-stress
+** Frustrated
+** Grieving
+** Happy
+** Jealous
+** Joyful
+** Loving
+** Relieved
+** Sad
+* Postural-state <nowiki>{requireChild}</nowiki>
+** Crouching
+** Kneeling
+** On-treadmill
+** Prone
+** Sitting
+** Standing
+** Supported-by-chin-rest
+* Agent-trait <nowiki>{requireChild, extensionAllowed}</nowiki>
+** Age
+*** <nowiki># {takesValue}</nowiki>
+** Sex
+*** <nowiki># {takesValue}</nowiki>
+** Handedness
+*** <nowiki># {takesValue}</nowiki>
+* Social-role <nowiki>{requireChild} [The role of the agent not particular to the task.]</nowiki>
+** Brother
+** Child
+** Father
+** Mother
+** Sister
+* Task-role <nowiki>{requireChild} [The role of the agent in the task.]</nowiki>
+** Experiment-controller
+** Experiment-subject
+** Experimenter
+** Follower
+** Friend
+** Leader
+** Stranger
+** Student
+** Teacher
+* Association <nowiki>{requireChild}</nowiki>
+** Associated-with-non-agent
+** Associated-with-other <nowiki>[Item such as a cup belonging to another person]</nowiki>
+** Associated-with-self <nowiki>[Item such as a cup belonging to the participant]</nowiki>
+
+'''Data-property'''
+* Statistical-property <nowiki>{requireChild}</nowiki>
+** Accuracy <nowiki>{requireChild}</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Data-mean <nowiki>{requireChild}</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Data-median <nowiki>{requireChild}</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Data-minimum <nowiki>{requireChild}</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Data-maximum <nowiki>{requireChild}</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Statistical-precision <nowiki>{requireChild}</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Statistical-recall <nowiki>{requireChild}</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Standard-deviation <nowiki>{requireChild} [As a Name:Value]</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+* Observational-property <nowiki>{requireChild} [Manually identified data feature. Should be grouped with a label of the form AgentID_featureName]</nowiki>
+** <nowiki># {takesValue}</nowiki>
+* Computed-property <nowiki>{requireChild} [Feature computed by tool. Should be grouped with a label of the form Toolname_propertyName]</nowiki>
+** <nowiki># {takesValue}</nowiki>
+
+'''Task-property'''
+* Attentional-strategy
+** Bottom-up-attention
+** Covert-attention
+** Divided-attention
+** Focused-attention
+** Orienting-attention
+** Overt-attention
+** Selective-attention
+** Sustained-attention
+** Top-down-attention
+* Task-event-type
+** Activity
+** Background-task
+** Experimental-stimulus <nowiki>[Part of something designed to elicit a response in the experiment.]</nowiki>
+** Failure
+** Feedback
+** Incidental
+** Instructional
+** Mishap <nowiki>[Unplanned disruption such as an equipment or experiment control abnormality or experimenter error.]</nowiki>
+** Participant-response <nowiki>[Something related to a participant's actions in performing the task.]</nowiki>
+** Primary-task
+** Warning <nowiki>[As in a warning message that you are getting too close to the shoulder in a driving task]</nowiki>
+* Task-effect-evidence
+** External-evidence <nowiki>[External judgment assumed to be ground truth such as from an experiment control software or an annotator about participant actions such as answering a question, failing to answer in time, etc. Related to participant indication]</nowiki>
+** Intended-effect <nowiki>[How the stimulus is expected to effect the participants based on the experimental design.]</nowiki>
+** Behavioral-evidence
+* Task-response-type
+** Appropriate
+** Correction
+** Erroneous
+** Imagined <nowiki>[This is used to identity that the (sub)event only happened in the imagination of the participant, e.g.  imagined movements in motor imagery paradigms.]</nowiki>
+** Inappropriate
+** Indeterminate
+** Missed
+** Near-miss
+** Successful
+* Task-stimulus-type
+** Cue
+** Distractor
+** Expected <nowiki>[Of low information value, for example frequent Non-targets in an RSVP paradigm]</nowiki>
+** Extraneous
+** Meaningful
+** Newly-learned
+** Non-informative
+** Non-target <nowiki>[Make sure to tag Expected if the Non-target is frequent]</nowiki>
+** Not-meaningful
+** Novel <nowiki>[Genuinely novel such as an event occurring once or so per experiment]</nowiki>
+** Oddball <nowiki>[Unexpected or infrequent]</nowiki>
+** Planned
+** Penalizing
+** Priming
+** Rewarding
+** Target <nowiki>[Something the subject is looking for]</nowiki>
+** Threatening
+** Timed
+** Unexpected
+
+'''Attribute''' <nowiki>{extensionAllowed} [Vocabulary tree for properties.]</nowiki>
+* Informational <nowiki>[Informational properties.]</nowiki>
+** Label <nowiki>{requireChild} [A string of 20 or fewer characters. Labels are used to define scopes or can be assigned to several events sharing the same characteristic (an event code).]</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** ID <nowiki>{requireChild} [An alphanumeric code usually used to identify particular objects.]</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Description <nowiki>{requireChild} [Human-readable text description of the event.]</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Definition <nowiki>[A tag used within a tag group also containing a Label tag to indicate that the Label represents that group of tags.]</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Metadata <nowiki>[A tag used within a tag group indicate to designate information about the data such as file size or author.]</nowiki>
+*** Creation-date <nowiki>{requireChild}</nowiki>
+**** <nowiki># {takesValue, unitClass=datetime}</nowiki>
+*** Library-name <nowiki>{requireChild}</nowiki>
+**** <nowiki># {takesValue}</nowiki>
+*** Subject <nowiki>{requireChild}</nowiki>
+**** <nowiki># {takesValue}</nowiki>
+*** Version <nowiki>{requireChild}</nowiki>
+**** <nowiki># {takesValue}</nowiki>
+** Parameter <nowiki>{requireChild} [As a Name:Value]</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+* Organizational
+** Block <nowiki>[An event used to indicate a particular organizational part in the experimental design or plan. Usually the Block tag is used with scoping tags as well as tags describing what the block represents to assist with downstream analysis.]</nowiki>
+** Collection <nowiki>[A tag designating a grouping of items such as in a set or list.]</nowiki>
+** Context <nowiki>[A tag indicating the overall environment and setup of the experiment or a portion of the experiment. This event is often inserted into a recording at the beginning of an experimental record to describe information such as whether the experiment is conducted indoors or outdoors or whether the experiment is conducted in the real-world or in a controlled laboratory setting. Tags specifying subject metadata might also be included.]</nowiki>
+** Condition-variable <nowiki>[An aspect of the experiment or task that is to be varied during the experiment. Condition-variables are sometimes called independent variables.]</nowiki>
+** Control-variable <nowiki>[An aspect of the experiment that is fixed throughout the study and usually is explicitly controlled.]</nowiki>
+** Data-acquisition <nowiki>[An activity related to data acquisition.]</nowiki>
+** Event-stream <nowiki>[An ordered list of events]</nowiki>
+** Namespace <nowiki>[Tag used to indicate a namespace type of definition.]</nowiki>
+** Permutation <nowiki>[A tag used within a tag group also containing a Label tag to indicate that the Label represents events that are permutations of each other.]</nowiki>
+** Response-variable <nowiki>[An aspect of the experiment or task that is measured as control-variables are varied during the experiment. Response-variables are sometimes called dependent variables.]</nowiki>
+** Sequence <nowiki>[A tag used within a tag group also containing a Label tag to indicate that the Label represents events related in a sequential order.]</nowiki>
+** Session
+** Task <nowiki>[A tag used to indicate a linkage to the task.]</nowiki>
+** Trial <nowiki>[A tag used to indicate a particular organizational part in the experimental design often containing a stimulus-response pair.]</nowiki>
+* Relational <nowiki>{extensionAllowed}</nowiki>
+** Comparative-relation
+*** Less-than
+*** Less-than-or-equal-to
+*** Greater-than
+*** Greater-than-or-equal-to
+*** Equal-to
+*** Not-equal-to
+** Connective-relation
+*** Belongs-to <nowiki>[As in owned by]</nowiki>
+*** Connected-to
+*** Contained-in
+*** Described-by
+*** From-to
+*** Group-of
+*** Implied-by
+*** Interacts-with
+*** Member-of
+*** Part-of
+*** Performed-by
+*** Related-to
+** Spatiotemporal-relation
+*** Directional-relation
+**** Away-from
+**** Backward
+**** Closing
+**** Decreasing
+**** Downward
+**** Forward
+**** Increasing
+**** Leftward
+**** Rightward
+**** Opening
+**** Towards
+**** Upward
+*** Orientational-relation
+**** Horizontally-oriented
+**** Oblique
+**** Rotated
+**** Vertically-oriented
+*** Positional-relation <nowiki>[Should be grouped with what it is positionally related to.]</nowiki>
+**** Above
+**** Across
+**** Adjacent-to
+**** Ahead-of
+**** Around
+**** Back-of
+**** Behind
+**** Below
+**** Between <nowiki>[Should be grouped with the items between designates.]</nowiki>
+**** Bilateral
+**** Bottom-of <nowiki>[At the bottom of something. Should be grouped with the item that it is positionally related to.]</nowiki>
+**** Boundary-of
+**** Center-of <nowiki>[At the center of something. Should be grouped with the item that it is positionally related to.]</nowiki>
+**** Close-to
+**** Distance-from <nowiki>[Should be grouped with an item from which the distance is measured. The resulting group will usually be grouped with an actual Distance or Angle value.]</nowiki>
+**** Edge-of
+**** Far-from
+**** Front-of
+**** Inside-of
+**** Left-of
+**** Left-side
+**** Outside-of
+**** Over
+**** Right-of
+**** Right-side
+**** Top-of
+**** Under
+*** Temporal-relation
+**** After
+**** Asynchronous-with
+**** Before
+**** During
+**** Synchronous-with
+**** Waiting-for
+*** Variability-relation
+**** Constant
+**** Continuous
+**** Deterministic
+**** Discrete
+**** Flickering <nowiki>[Irregular changes in time]</nowiki>
+**** Repetitive
+**** Stochastic
+**** Sudden
+**** Varying
+** Thermal-relation
+*** Cold
+*** Colder-than
+*** Hot
+*** Hotter-than
+* Quantitative <nowiki>{extensionAllowed}</nowiki>
+** Angle <nowiki>{requireChild} [Angle assumed clockwise from vertical unless grouped with axis.]</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Fraction <nowiki>{requireChild} [Fraction of items of a particular type.]</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Interval
+*** <nowiki># {takesValue}</nowiki>
+** Item-count <nowiki>{requireChild}</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Percentage <nowiki>{requireChild} [Percentage of items of a particular type.]</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Probability <nowiki>[Use to specify the level of certainty about the occurrence of the event.]</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Ratio <nowiki>{requireChild}</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Repetition <nowiki>{requireChild} [When the same type of event such as a fixation on the exact same object happens multiple times and it might be necessary to distinguish the first look vs. others]</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Uncertainty <nowiki>{requireChild}</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+* Categorical <nowiki>{extensionAllowed}</nowiki>
+** All
+** Congruent <nowiki>{relatedTag=Incongruent}</nowiki>
+** Constrained <nowiki>{relatedTag=Unconstrained}</nowiki>
+** Correct
+** Deep
+** False
+** High
+** Incongruent
+** Incorrect
+** Invalid
+** Liminal
+** Low
+** Masked <nowiki>[Ability to perceive influenced by presence of another stimulus]</nowiki>
+** Medium
+** None
+** Normal
+** Negative
+** Positive
+** Shallow
+** Some
+** Subliminal
+** Supraliminal
+** Symmetric <nowiki>{relatedTag=Unsymmetric}</nowiki>
+** True
+** Unconstrained <nowiki>{relatedTag=Constrained}</nowiki>
+** Unmasked <nowiki>[Ability to perceive not influenced by presence of another stimulus]</nowiki>
+** Unsymmetric <nowiki>{relatedTag=Symmetric}</nowiki>
+** Valid
+* Descriptive <nowiki>[Indicate the type of thing.]</nowiki>
+** Abstract
+** Biological
+** Clinical
+** Complex
+** Composite
+** Electrical
+** Fractal
+** Involuntary
+** Mechanical
+** Magnetic
+** Numerical
+** Random
+** Symbolic
+* Sensory <nowiki>{extensionAllowed}</nowiki>
+** Auditory
+*** Audible
+*** Frequency <nowiki>{requireChild}</nowiki>
+**** <nowiki># {takesValue, isNumeric, unitClass=frequency}</nowiki>
+*** Loudness <nowiki>{requireChild}</nowiki>
+**** <nowiki># {takesValue, isNumeric}</nowiki>
+*** Monophonic <nowiki>[Consisting of one independent audio channel]</nowiki>
+*** Ramp-up <nowiki>[Increasing in amplitude]</nowiki>
+*** Ramp-down <nowiki>[Decreasing in amplitude]</nowiki>
+*** Silent
+*** Stereophonic <nowiki>[Consisting of more than one independent audio channel]</nowiki>
+*** Timbre <nowiki>[Sound quality]</nowiki>
+** Gustatory <nowiki>[Pertaining to sensations of relating to taste.]</nowiki>
+*** Bitter
+*** Salty
+*** Savory
+*** Sour
+*** Sweet
+** Tactile <nowiki>[Pertaining to sensations of relating to touch.]</nowiki>
+*** Tactile-pressure
+*** Tactile-texture
+*** Tactile-vibration
+** Olfactory <nowiki>[Pertaining to sensations of relating to smell.]</nowiki>
+** Somatic <nowiki>[Pertaining to sensations of nervous system such as pain.]</nowiki>
+*** Pain
+*** Stress
+** Vestibular
+** Visual
+*** Luminance <nowiki>{requireChild}</nowiki>
+*** Color <nowiki>{requireChild}</nowiki>
+**** Color-shade
+***** Dark-shade
+***** Light-shade
+**** HSV-color
+***** Hue
+****** <nowiki># {takesValue, isNumeric} [Angular value between 0 and 360]</nowiki>
+***** Saturation
+****** <nowiki># {takesValue, isNumeric} [B value of RGB between 0 and 1]</nowiki>
+***** HSV-value
+****** <nowiki># {takesValue, isNumeric} [G value of RGB between 0 and 1]</nowiki>
+**** RGB-color
+***** RGB-red
+****** <nowiki># {takesValue, isNumeric} [R value of RGB between 0 and 1]</nowiki>
+***** RGB-blue
+****** <nowiki># {takesValue, isNumeric} [B value of RGB between 0 and 1]</nowiki>
+***** RGB-green
+****** <nowiki># {takesValue, isNumeric} [G value of RGB between 0 and 1]</nowiki>
+**** Grayscale <nowiki>[Indicates gray scale]</nowiki>
+***** <nowiki># {takesValue, isNumeric} [White intensity between 0 and 1]</nowiki>
+**** CSS-color <nowiki>[One of 140 colors supported by all browsers. For more details such as the color RGB or HEX values,  check:  https://www.w3schools.com/colors/colors_groups.asp]</nowiki>
+***** Blue-color
+****** CadetBlue
+****** SteelBlue
+****** LightSteelBlue
+****** LightBlue
+****** PowderBlue
+****** LightSkyBlue
+****** SkyBlue
+****** CornflowerBlue
+****** DeepSkyBlue
+****** DodgerBlue
+****** RoyalBlue
+****** Blue
+****** MediumBlue
+****** DarkBlue
+****** Navy
+****** MidnightBlue
+***** Brown-color
+****** Cornsilk
+****** BlanchedAlmond
+****** Bisque
+****** NavajoWhite
+****** Wheat
+****** BurlyWood
+****** Tan
+****** RosyBrown
+****** SandyBrown
+****** GoldenRod
+****** DarkGoldenRod
+****** Peru
+****** Chocolate
+****** Olive
+****** SaddleBrown
+****** Sienna
+****** Brown
+****** Maroon
+***** Cyan-color
+****** Aqua
+****** Cyan
+****** LightCyan
+****** PaleTurquoise
+****** Aquamarine
+****** Turquoise
+****** MediumTurquoise
+****** DarkTurquoise
+***** Green-color
+****** GreenYellow
+****** Chartreuse
+****** LawnGreen
+****** Lime
+****** LimeGreen
+****** PaleGreen
+****** LightGreen
+****** MediumSpringGreen
+****** SpringGreen
+****** MediumSeaGreen
+****** SeaGreen
+****** ForestGreen
+****** Green
+****** DarkGreen
+****** YellowGreen
+****** OliveDrab
+****** DarkOliveGreen
+****** MediumAquaMarine
+****** DarkSeaGreen
+****** LightSeaGreen
+****** DarkCyan
+****** Teal
+***** Gray-color
+****** Gainsboro
+****** LightGray
+****** Silver
+****** DarkGray
+****** DimGray
+****** Gray
+****** LightSlateGray
+****** SlateGray
+****** DarkSlateGray
+****** Black
+***** Orange-color
+****** Orange
+****** DarkOrange
+****** Coral
+****** Tomato
+****** OrangeRed
+***** Pink-color
+****** Pink
+****** LightPink
+****** HotPink
+****** DeepPink
+****** PaleVioletRed
+****** MediumVioletRed
+***** Purple-color
+****** Lavender
+****** Thistle
+****** Plum
+****** Orchid
+****** Violet
+****** Fuchsia
+****** Magenta
+****** MediumOrchid
+****** DarkOrchid
+****** DarkViolet
+****** BlueViolet
+****** DarkMagenta
+****** Purple
+****** MediumPurple
+****** MediumSlateBlue
+****** SlateBlue
+****** DarkSlateBlue
+****** RebeccaPurple
+****** Indigo
+***** Red-color
+****** LightSalmon
+****** Salmon
+****** DarkSalmon
+****** LightCoral
+****** IndianRed
+****** Crimson
+****** Red
+****** FireBrick
+****** DarkRed
+***** Yellow-color
+****** Gold
+****** Yellow
+****** LightYellow
+****** LemonChiffon
+****** LightGoldenRodYellow
+****** PapayaWhip
+****** Moccasin
+****** PeachPuff
+****** PaleGoldenRod
+****** Khaki
+****** DarkKhaki
+***** White-color
+****** White
+****** Snow
+****** HoneyDew
+****** MintCream
+****** Azure
+****** AliceBlue
+****** GhostWhite
+****** WhiteSmoke
+****** SeaShell
+****** Beige
+****** OldLace
+****** FloralWhite
+****** Ivory
+****** AntiqueWhite
+****** Linen
+****** LavenderBlush
+****** MistyRose
+*** View
+**** 2D-view
+**** 3D-view
+**** Background-view
+**** Bistable-view <nowiki>[Something having two stable visual forms that have two distinguishable stable forms as in optical illusions.]</nowiki>
+**** Foreground-view
+**** Foveal-view <nowiki>[Visual presentation directly on the fovea.]</nowiki>
+**** Map-view <nowiki>[A representation of a geographical location.]</nowiki>
+***** Aerial-view <nowiki>[A representation from an overhead view such as captured by a drone.]</nowiki>
+***** Satellite-view <nowiki>[A representation as captured by technology such as a satellite.]</nowiki>
+***** Street-view <nowiki>[A panoramic view from a position on the ground.]</nowiki>
+**** Peripheral-view
+* Spatiotemporal <nowiki>{extensionAllowed}</nowiki>
+** Spatial
+*** Direction
+**** <nowiki># {takesValue, isNumeric}</nowiki>
+*** Distance <nowiki>{requireChild} [Distance from a specified position in a specified Direction]</nowiki>
+**** <nowiki># {takesValue, isNumeric, unitClass=physicalLength}</nowiki>
+*** Orientation
+**** <nowiki># {takesValue, isNumeric}</nowiki>
+*** Position <nowiki>{requireChild} [Coordinates with respect a specified frame of reference or the default Screen-frame if no frame is given.]</nowiki>
+**** X-position <nowiki>{requireChild}</nowiki>
+***** <nowiki># {takesValue, isNumeric, unitClass=physicalLength}</nowiki>
+**** Y-position <nowiki>{requireChild}</nowiki>
+***** <nowiki># {takesValue, isNumeric, unitClass=physicalLength}</nowiki>
+**** Z-position <nowiki>{requireChild}</nowiki>
+***** <nowiki># {takesValue, isNumeric, unitClass=physicalLength}</nowiki>
+*** Reference-frame <nowiki>[A frame of reference used to express spatial attributes.]</nowiki>
+**** Custom-frame <nowiki>[A custom frame of reference which should be defined in terms of one of the default frames of reference or by a library.]</nowiki>
+**** Room-frame
+**** Screen-frame <nowiki>[HED default frame of reference with origin at center of screen used to display experimental stimuli. Looking out of the screen has +X is horizontal to the right. +Y is up and +Z is out of the front of the screen.]</nowiki>
+**** Screen-facing-frame <nowiki>[HED default frame of reference with origin at the subject Nasion. +X is horizontal to the subject right. +Y is to the subject front and +Z is vertical upward.]</nowiki>
+**** World-frame
+*** Size <nowiki>{requireChild}</nowiki>
+**** Area <nowiki>{requireChild}</nowiki>
+***** <nowiki># {takesValue, isNumeric, unitClass=area}</nowiki>
+**** Length <nowiki>{requireChild} [A measurement of the size along the longest dimension.]</nowiki>
+***** <nowiki># {takesValue, isNumeric, unitClass=physicalLength}</nowiki>
+**** Width <nowiki>[A measurement along the shortest of two or three dimensions.]</nowiki>
+***** <nowiki># {takesValue, isNumeric, unitClass=physicalLength}</nowiki>
+**** Height <nowiki>[A measurement in a direction perpendicular to Length and Width in 3D. In 2D, it is perpendicular to both the Length and Height.]</nowiki>
+***** <nowiki># {takesValue, isNumeric, unitClass=physicalLength}</nowiki>
+**** Volume <nowiki>{requireChild}</nowiki>
+***** <nowiki># {takesValue, isNumeric, unitClass=volume}</nowiki>
+*** Surface-properties
+**** Rough
+**** Smooth
+** Resolution
+*** IO-resolution <nowiki>{requireChild}</nowiki>
+**** Screen-resolution <nowiki>{requireChild} [Number of pixels in each dimension for a digital display device.]</nowiki>
+***** <nowiki># {takesValue}</nowiki>
+**** Printer-resolution <nowiki>{requireChild} [Number of dots-per-inch for a printer.]</nowiki>
+***** <nowiki># {takesValue}</nowiki>
+*** Spatial-resolution <nowiki>{requireChild} [Linear spacing of a spatial measurement.]</nowiki>
+**** <nowiki># {takesValue}</nowiki>
+*** Spectral-resolution <nowiki>{requireChild}</nowiki>
+**** <nowiki># {takesValue}</nowiki>
+*** Temporal-resolution <nowiki>{requireChild}</nowiki>
+**** <nowiki># {takesValue}</nowiki>
+** Temporal
+*** Delay <nowiki>[Indicator of some amount of time elapsed before something happens.]</nowiki>
+**** <nowiki># {takesValue, isNumeric, unitClass=time}</nowiki>
+*** Duration <nowiki>{requireChild} [An offset that is implicit after duration time passed from the onset]</nowiki>
+**** <nowiki># {takesValue, isNumeric, unitClass=time}</nowiki>
+*** Onset
+*** Offset
+*** Pause <nowiki>[Indicating something stopped such as a break or waiting for input.]</nowiki>
+*** Response-start-delay <nowiki>{requireChild} [How long the start of the response is delayed from the stimulus onset.]</nowiki>
+**** <nowiki># {takesValue, isNumeric, unitClass=time}</nowiki>
+*** Response-end-delay <nowiki>{requireChild} [How long the end of the response is delayed from the stimulus onset.]</nowiki>
+**** <nowiki># {takesValue, isNumeric, unitClass=time}</nowiki>
+*** Temporal-value <nowiki>{requireChild}</nowiki>
+**** <nowiki># {takesValue, isNumeric, unitClass=time}</nowiki>
+*** Time-out
+*** Time-sync <nowiki>[Indicates a marker inserted into the recorded data to allow post hoc synchronization of concurrently recorded data streams.]</nowiki>
+** Rate-of-change
+*** Acceleration
+**** <nowiki># {takesValue, isNumeric, unitClass=acceleration}</nowiki>
+*** Jerk-rate <nowiki>[Rate at which an object's acceleration changes with respect to time.]</nowiki>
+**** <nowiki># {takesValue, isNumeric, unitClass=jerk}</nowiki>
+*** Sampling-rate <nowiki>{requireChild} [Number of measurements per unit time.]</nowiki>
+**** <nowiki># {takesValue, unitClass=frequency}</nowiki>
+*** Refresh-rate <nowiki>{requireChild} [Number of times per second a device displays an image. Also known as the frame rate.]</nowiki>
+**** <nowiki># {takesValue, isNumeric}</nowiki>
+*** Temporal-rate <nowiki>[Number of occurrences in a unit of time.]</nowiki>
+**** <nowiki># {takesValue, isNumeric}</nowiki>
+*** Velocity
+**** <nowiki># {takesValue, isNumeric, unitClass=speed}</nowiki>
+* Thermodynamic
+** Energy
+** Pressure
+** Temperature
+*** <nowiki># {takesValue, isNumeric}</nowiki>
+* Environmental <nowiki>{extensionAllowed}</nowiki>
+** Indoors
+** Outdoors
+** Real-world
+** Virtual-world
+** Augmented-reality
+** Motion-platform
+** Urban
+** Rural
+*** Wooden-walkway
+** Terrain
+*** Composite-terrain
+*** Dirt-terrain
+*** Grassy-terrain
+*** Gravel-terrain
+*** Leaf-covered-terrain
+*** Muddy-terrain
+*** Paved-terrain
+*** Rocky-terrain
+*** Sloped-terrain
+*** Uneven-terrain
+* Anatomical
+** Body-part
+*** Head
+**** Hair
+**** Ear
+**** Face
+***** Cheek
+***** Chin
+***** Eye
+***** Eyebrow
+***** Forehead
+***** Lip
+***** Nose
+***** Mouth
+***** Teeth
+*** Arm
+**** Elbow
+**** Hand
+***** Finger
+****** Index-finger
+****** Little-finger
+****** Middle-finger
+****** Ring-finger
+****** Thumb
+**** Wrist
+*** Leg
+**** Ankle
+**** Foot
+***** Toe
+**** Knee
+*** Torso
+**** Upper-torso
+***** Chest
+**** Lower-torso
+***** Hips
+**** Waist
+
+!# end schema
+
+'''Unit classes'''
+* time <nowiki>{defaultUnits=s}</nowiki>
+** second <nowiki>{SIUnit}</nowiki>
+** s <nowiki>{SIUnit, unitSymbol}</nowiki>
+** day
+** minute
+** hour
+* dateTime <nowiki>{defaultUnits=YYYY-MM-DDThh:mm:ss}</nowiki>
+** YYYY-MM-DDThh:mm:ss
+* clockTime <nowiki>{defaultUnits=hour:min}</nowiki>
+** hour:min
+** hour:min:sec
+* frequency <nowiki>{defaultUnits=Hz}</nowiki>
+** hertz <nowiki>{SIUnit}</nowiki>
+** Hz <nowiki>{SIUnit, unitSymbol}</nowiki>
+* angle <nowiki>{defaultUnits=radian}</nowiki>
+** radian <nowiki>{SIUnit}</nowiki>
+** rad <nowiki>{SIUnit, unitSymbol}</nowiki>
+** degree
+* physicalLength <nowiki>{defaultUnits=m}</nowiki>
+** metre <nowiki>{SIUnit}</nowiki>
+** m <nowiki>{SIUnit, unitSymbol}</nowiki>
+** foot
+** mile
+* pixels <nowiki>{defaultUnits=px}</nowiki>
+** pixel
+** px <nowiki>{unitSymbol}</nowiki>
+* area <nowiki>{defaultUnits=m^2}</nowiki>
+** m^2 <nowiki>{SIUnit, unitSymbol}</nowiki>
+** px^2 <nowiki>{unitSymbol}</nowiki>
+** pixel^2
+* volume <nowiki>{defaultUnits=m^3}</nowiki>
+** m^3 <nowiki>{SIUnit, unitSymbol}</nowiki>
+* speed <nowiki>{defaultUnits=m-per-s}</nowiki>
+** m-per-s <nowiki>{SIUnit, unitSymbol}</nowiki>
+** mph <nowiki>{unitSymbol}</nowiki>
+** kph <nowiki>{unitSymbol}</nowiki>
+* acceleration <nowiki>{defaultUnits=m-per-s^2}</nowiki>
+** m-per-s^2 <nowiki>{SIUnit, unitSymbol}</nowiki>
+* jerk <nowiki>{defaultUnits=m-per-s^3}</nowiki>
+** m-per-s^3 <nowiki>{unitSymbol}</nowiki>
+* intensity <nowiki>{defaultUnits=dB}</nowiki>
+** dB <nowiki>{unitSymbol}</nowiki>
+* luminousIntensity <nowiki>{defaultUnits=cd}</nowiki>
+** candela <nowiki>{SIUnit}</nowiki>
+** cd <nowiki>{SIUnit, unitSymbol}</nowiki>
+* memorySize <nowiki>{defaultUnits=B}</nowiki>
+** byte <nowiki>{SIUnit}</nowiki>
+** B <nowiki>{SIUnit, unitSymbol}</nowiki>
+* currency <nowiki>{defaultUnits=$}</nowiki>
+** dollar
+** $ <nowiki>{unitSymbol}</nowiki>
+** point
+** fraction
+
+'''Unit modifiers'''
+* deca <nowiki>{SIUnitModifier} [SI unit multiple representing 10^1]</nowiki>
+* da <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^1]</nowiki>
+* hecto <nowiki>{SIUnitModifier} [SI unit multiple representing 10^2]</nowiki>
+* h <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^2]</nowiki>
+* kilo <nowiki>{SIUnitModifier} [SI unit multiple representing 10^3]</nowiki>
+* k <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^3]</nowiki>
+* mega <nowiki>{SIUnitModifier} [SI unit multiple representing 10^6]</nowiki>
+* M <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^6]</nowiki>
+* giga <nowiki>{SIUnitModifier} [SI unit multiple representing 10^9]</nowiki>
+* G <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^9]</nowiki>
+* tera <nowiki>{SIUnitModifier} [SI unit multiple representing 10^12]</nowiki>
+* T <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^12]</nowiki>
+* peta <nowiki>{SIUnitModifier} [SI unit multiple representing 10^15]</nowiki>
+* P <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^15]</nowiki>
+* exa <nowiki>{SIUnitModifier} [SI unit multiple representing 10^18]</nowiki>
+* E <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^18]</nowiki>
+* zetta <nowiki>{SIUnitModifier} [SI unit multiple representing 10^21]</nowiki>
+* Z <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^21]</nowiki>
+* yotta <nowiki>{SIUnitModifier} [SI unit multiple representing 10^24]</nowiki>
+* Y <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^24]</nowiki>
+* deci <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-1]</nowiki>
+* d <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-1]</nowiki>
+* centi <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-2]</nowiki>
+* c <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-2]</nowiki>
+* milli <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-3]</nowiki>
+* m <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-3]</nowiki>
+* micro <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-6]</nowiki>
+* u <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-6]</nowiki>
+* nano <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-9]</nowiki>
+* n <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-9]</nowiki>
+* pico <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-12]</nowiki>
+* p <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-12]</nowiki>
+* femto <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-15]</nowiki>
+* f <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-15]</nowiki>
+* atto <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-18]</nowiki>
+* a <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-18]</nowiki>
+* zepto <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-21]</nowiki>
+* z <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-21]</nowiki>
+* yocto <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-24]</nowiki>
+* y <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-24]</nowiki>
+!# end hed
+
+This is an epilogue.
+This is a second line of an epilogue.
\ No newline at end of file
diff --git a/hedtools/tests/data/invalid_wiki_schemas/HED_header_bad_library.mediawiki b/hedtools/tests/data/invalid_wiki_schemas/HED_header_bad_library.mediawiki
new file mode 100644
index 000000000..220848a42
--- /dev/null
+++ b/hedtools/tests/data/invalid_wiki_schemas/HED_header_bad_library.mediawiki
@@ -0,0 +1,2 @@
+HED library:invalidlib1293
+
diff --git a/hedtools/tests/data/invalid_wiki_schemas/HED_header_invalid.mediawiki b/hedtools/tests/data/invalid_wiki_schemas/HED_header_invalid.mediawiki
new file mode 100644
index 000000000..7fdb80735
--- /dev/null
+++ b/hedtools/tests/data/invalid_wiki_schemas/HED_header_invalid.mediawiki
@@ -0,0 +1,2 @@
+HED version:8.0.0-alpha.1:3
+
diff --git a/hedtools/tests/data/invalid_wiki_schemas/HED_header_invalid_version.mediawiki b/hedtools/tests/data/invalid_wiki_schemas/HED_header_invalid_version.mediawiki
new file mode 100644
index 000000000..51f07e783
--- /dev/null
+++ b/hedtools/tests/data/invalid_wiki_schemas/HED_header_invalid_version.mediawiki
@@ -0,0 +1,2 @@
+HED version:8.0
+
diff --git a/hedtools/tests/data/invalid_wiki_schemas/HED_header_missing.mediawiki b/hedtools/tests/data/invalid_wiki_schemas/HED_header_missing.mediawiki
new file mode 100644
index 000000000..56f12cff5
--- /dev/null
+++ b/hedtools/tests/data/invalid_wiki_schemas/HED_header_missing.mediawiki
@@ -0,0 +1,3 @@
+This file has no header and is clearly invalid
+
+!# start schema
\ No newline at end of file
diff --git a/hedtools/tests/data/invalid_wiki_schemas/HED_header_missing_version.mediawiki b/hedtools/tests/data/invalid_wiki_schemas/HED_header_missing_version.mediawiki
new file mode 100644
index 000000000..d95c7f970
--- /dev/null
+++ b/hedtools/tests/data/invalid_wiki_schemas/HED_header_missing_version.mediawiki
@@ -0,0 +1,2 @@
+HED library:validlib
+
diff --git a/hedtools/tests/data/invalid_wiki_schemas/HED_hed_no_end.mediawiki b/hedtools/tests/data/invalid_wiki_schemas/HED_hed_no_end.mediawiki
new file mode 100644
index 000000000..3373bda51
--- /dev/null
+++ b/hedtools/tests/data/invalid_wiki_schemas/HED_hed_no_end.mediawiki
@@ -0,0 +1,9 @@
+HED version:8.0.0-alpha.1
+
+
+This is a prologue line.
+This is a second prologue line.
+
+!# start schema
+
+!# end schema
diff --git a/hedtools/tests/data/invalid_wiki_schemas/HED_schema_no_end.mediawiki b/hedtools/tests/data/invalid_wiki_schemas/HED_schema_no_end.mediawiki
new file mode 100644
index 000000000..b23f649b4
--- /dev/null
+++ b/hedtools/tests/data/invalid_wiki_schemas/HED_schema_no_end.mediawiki
@@ -0,0 +1,7 @@
+HED version:8.0.0-alpha.1
+
+
+This is a prologue line.
+This is a second prologue line.
+
+!# start schema
\ No newline at end of file
diff --git a/hedtools/tests/data/invalid_wiki_schemas/HED_schema_no_start.mediawiki b/hedtools/tests/data/invalid_wiki_schemas/HED_schema_no_start.mediawiki
new file mode 100644
index 000000000..7714d0bc2
--- /dev/null
+++ b/hedtools/tests/data/invalid_wiki_schemas/HED_schema_no_start.mediawiki
@@ -0,0 +1,6 @@
+HED version:8.0.0-alpha.1
+
+
+This is a prologue line.
+This is a second prologue line.
+
diff --git a/hedtools/tests/data/invalid_wiki_schemas/HED_schema_out_of_order.mediawiki b/hedtools/tests/data/invalid_wiki_schemas/HED_schema_out_of_order.mediawiki
new file mode 100644
index 000000000..52d96052b
--- /dev/null
+++ b/hedtools/tests/data/invalid_wiki_schemas/HED_schema_out_of_order.mediawiki
@@ -0,0 +1,9 @@
+HED version:8.0.0-alpha.1
+
+
+This is a prologue line.
+This is a second prologue line.
+
+!# end schema
+
+!# start schema
\ No newline at end of file
diff --git a/hedtools/tests/data/invalid_wiki_schemas/HED_separator_invalid.mediawiki b/hedtools/tests/data/invalid_wiki_schemas/HED_separator_invalid.mediawiki
new file mode 100644
index 000000000..ba6a06514
--- /dev/null
+++ b/hedtools/tests/data/invalid_wiki_schemas/HED_separator_invalid.mediawiki
@@ -0,0 +1,1106 @@
+HED version:8.0.0-alpha.1
+
+
+This is a prologue line.
+This is a second prologue line.
+
+!# start schema
+
+'''Event'''
+* Sensory-event <nowiki>[Something perceivable by the participant. An event meant to be an experimental stimulus should also be tagged with Attribute/Contextual/Task-related. Other events may be tagged with Attribute/Contextual/Non task-related.]</nowiki>
+* Agent-action <nowiki>[Any action engaged in by an agent. An event which is a participant response to an experimental stimulus should also be tagged with Attribute/Task-related and Attribute/Role/Experimental subject.]</nowiki>
+* Data-feature <nowiki>[An event derived from the data itself, possibly inserted in a post-hoc fashion, for example an interictal spike or alpha burst.]</nowiki>
+* Experiment-control <nowiki>{extensionAllowed} [An event pertaining to the control of the experiment during its operation.]</nowiki>
+* Experiment-procedure <nowiki>[An event indicating an experimental procedure as in doing a saliva swab on the person during the experiment or administering a survey.]</nowiki>
+* Experiment-structure <nowiki>{extensionAllowed} [An event specifying the structure of the experiment.]</nowiki>
+
+'''Agent''' <nowiki>{extensionAllowed}</nowiki>
+* Animal-agent
+* Avatar-agent
+* Computational-agent
+* Controller-agent <nowiki>[An outside controller such as the experimental control software.]</nowiki>
+* Human-agent
+* Robotic-agent
+
+'''Action''' <nowiki>{extensionAllowed} [May or may not be in response to a previous Sensory presentation event.]</nowiki>
+* Move
+** Breathe
+*** Inhale <nowiki>[A deliberate inbreath, e.g. in response to a task instruction. If incidental, group it with Attribute/Cognitive modifier/Incidental. E.g. (Action/Move/Breathe/Inhale, Attribute/Cognitive modifier/Incidental).]</nowiki>
+*** Exhale <nowiki>[A deliberate outbreath, e.g. in response to a task instruction. If incidental, group it with Attribute/Cognitive modifier/Incidental. E.g. (Action/Move/Breathe/Exhale, Attribute/Cognitive modifier/Incidental).]</nowiki>
+*** Hold-breath
+** Move-full-body
+*** Bend
+*** Dance
+*** Fall-down <nowiki>[Involuntary fall to floor or ground]</nowiki>
+*** Flex
+*** Jerk <nowiki>[When some force applied to the participant causes them to involuntarily accommodate or react to it.]</nowiki>
+*** Recover-balance <nowiki>[A movement made in response to a stumble or unforeseen loss of balance]</nowiki>
+*** Stretch <nowiki>[Stretching the body or body part]</nowiki>
+*** Shudder
+*** Stumble <nowiki>[Temporary and involuntary loss of balance]</nowiki>
+*** Turn
+** Move-head
+*** Move-eyes
+**** Blink
+**** Close-eyes
+**** Fixate
+**** Inhibit-blinks
+**** Open-eyes
+**** Saccade
+*** Move-mouth
+**** Burp
+**** Clear-throat
+**** Cough
+**** Gurgle
+**** Hiccup
+**** Smile
+**** Sneeze
+**** Sniffle
+**** Swallow
+**** Yawn
+*** Nod
+*** Shake-head
+** Move-arm-hand
+*** Clap
+*** Drop <nowiki>[For example the participant drops a handheld ball]</nowiki>
+*** Grab
+*** Grasp <nowiki>[Grasp an object in one or both hands]</nowiki>
+*** Hold-down
+*** Lift <nowiki>[Often grouped with item being lifted.]</nowiki>
+*** Make-fist
+*** Point <nowiki>[Should be grouped with Item/Agent/Human/Body part/Hand/Finger tag to specify which finger was used for the action.]</nowiki>
+*** Press <nowiki>[Often grouped with body part doing action and item being pressed.]</nowiki>
+*** Push
+*** Reach <nowiki>[Requires a spatial goal such as reaching to touch a button or to grasp something. A body stretch is not a reach.]</nowiki>
+*** Release <nowiki>[Often grouped with item being released.]</nowiki>
+*** Retract <nowiki>[Often grouped with body part doing action and item being retracted.]</nowiki>
+*** Scratch
+*** Snap-fingers
+*** Tap <nowiki>[Often grouped with body part doing action.]</nowiki>
+*** Touch <nowiki>[May follow a Reach event]</nowiki>
+** Move-leg-foot
+*** Curl-toes
+*** Jog
+*** Jump
+*** Kick
+*** Pedal
+*** Run
+*** Step
+**** Heel-strike
+**** Toe-off
+*** Trot
+*** Walk
+* Communicate
+** Communicate-vocally
+*** Cry
+*** Groan
+*** Gulp <nowiki>[Communicate anxiety. Use swallow if non-editorial.]</nowiki>
+*** Laugh
+*** Scream
+*** Sigh
+*** Speak <nowiki>[Communicate using spoken language.]</nowiki>
+** Gesture <nowiki>[Make a expressive communicative movement of hands/arms and, often, face]</nowiki>
+*** Index-up
+*** Pump-fist
+*** Spread-hand
+*** Shhh
+*** Shrug
+*** Thumb-up
+*** Wave
+** Communicate-musically
+*** Hum
+*** Sing
+*** Vocalize <nowiki>[Make a sustained vowel sound such as, Ahhh]</nowiki>
+*** Whistle
+* Perform <nowiki>[Carry out or accomplish an action or task or function.]</nowiki>
+** Change
+** Collide
+** Fill-out-survey
+** Operate <nowiki>[Control something else.]</nowiki>
+*** Drive <nowiki>[Control something that moves.]</nowiki>
+*** Brake <nowiki>[Apply brakes to stop something.]</nowiki>
+*** Steer
+** Play <nowiki>[As a musical instrument or a game.]</nowiki>
+** Rest
+* Think
+** Allow <nowiki>[Allow access to something such as allowing a car to pass]</nowiki>
+** Attend-to
+** Count
+** Deny <nowiki>[Deny access to something such as preventing someone to pass]</nowiki>
+** Detect
+** Discriminate
+** Encode
+** Evade
+** Generate
+** Identify
+** Imagine
+** Read
+** Recall
+** Repeat
+** Switch-attention
+** Track
+
+'''Item''' <nowiki>{extensionAllowed}</nowiki>
+* Object <nowiki>{extensionAllowed} [Physical object perceptible via Visual, Auditory, Olfactory, and/or Tactile modalities.]</nowiki>
+** Geometric
+*** Pattern
+**** Dots
+**** Led-pattern
+*** 2D-shape
+**** Clockface
+**** Cross
+**** Ellipse
+***** Circle
+**** Rectangle
+***** Square
+**** Single-point
+**** Star
+**** Triangle
+*** 3D-shape
+**** Box
+***** Cube
+**** Cone
+**** Cylinder
+**** Ellipsoid
+***** Sphere
+**** Pyradmid
+** Man-made-object <nowiki>[A manmade object]</nowiki>
+*** Media
+**** Sound-clip
+**** Visualization <nowiki>[A visual representation of something else -- such as using a stick-figure to represent motion capture data.]</nowiki>
+***** Animation
+***** Art-installation
+***** Braille
+***** Cutout
+***** Image <nowiki>[A still 2-D representation.]</nowiki>
+****** Cartoon
+****** Drawing
+****** Icon
+****** Painting
+****** Photograph
+***** Movie
+***** Outline-visualization
+***** Point-light-visualization
+***** Sculpture
+***** Stick-figure-visualization
+*** Building <nowiki>[Whole building or building feature]</nowiki>
+**** Room
+**** Roof
+**** Entrance
+**** Attic
+**** Basement
+*** Clothing
+*** Device
+**** Computing-device
+**** Engine
+**** IO-device
+***** Input-device
+****** Button
+****** Joystick
+****** Keyboard
+******* Key <nowiki>{takesValue}</nowiki>
+******** <nowiki>#  [Value of key.]</nowiki>
+****** Keypad <nowiki>{takesValue}</nowiki>
+******* <nowiki>#  [Value of keypad key.]</nowiki>
+****** Microphone
+****** Mouse
+******* Mouse-button
+******* Scroll-wheel
+****** Touch-screen
+***** Network-device
+***** Output-device
+****** Display-device
+******* Head-mounted-display
+******* LED-display
+******* Screen
+******** Screen-window
+****** Auditory-device
+******* Headphones
+******* Screen-speaker
+******* Loudspeaker
+***** Recording-device
+****** EEG-recording
+****** File-recording
+****** MEG-recording
+****** Motion-capture
+****** Tape-recording
+**** Machine
+***** Noisemaker
+**** Measurement-device
+***** Clock
+****** Clock-face
+**** Robot
+**** Tool
+*** Document <nowiki>[A physical, written document]</nowiki>
+**** Manuscript
+**** Letter
+**** Note
+**** Book
+**** Notebook
+*** Furnishing <nowiki>[Furniture and appliances and other movable accessories including curtains and rugs]</nowiki>
+*** Navigational-object
+**** Path
+**** Road
+***** Lane
+**** Runway
+**** Sign
+*** Vehicle
+**** Aircraft
+**** Bicycle
+**** Boat
+**** Car
+**** Cart
+**** Tractor
+**** Train
+**** Truck
+** Natural-object
+*** Organism
+**** Animal
+**** Human
+**** Plant
+*** Mineral
+*** Natural-feature
+**** Field
+**** Hill
+**** Mountain
+**** River
+**** Waterfall
+** Food
+** Drink
+* Language <nowiki>[Perceivable in a visual or auditory (spoken) sensory presentation]</nowiki>
+** Character
+** Glyph
+** Nonword
+** Phoneme
+** Phrase
+** Sentence
+** Syllable
+** Textblock
+** Word
+* Sound <nowiki>[Item/Sound can be used in a tag group to describe, for example, sound created by an action Item/Sound, Action/Move/Move leg-foot/Walk, Agent/Human or an object or Item/Sound, Item/Object/Man-made/Vehicle/Car]</nowiki>
+** Environmental-sound <nowiki>[A background sound from the surrounding environment.]</nowiki>
+*** Crowd sound <nowiki>[Multiple speech streams at once]</nowiki>
+*** Noise
+**** White-noise
+**** Colored-noise
+** Musical-sound <nowiki>[Any tone or combination of tones with characteristics such as controlled pitch and timbre that can be controlled by an agent.]</nowiki>
+*** Tone
+*** Instrument-sound
+*** Vocalized-sound
+** Named-animal-sound
+*** Bark
+*** Bleat
+*** Crow
+*** Chirp
+*** Growl
+*** Meow
+*** Moo
+*** Purr
+*** Roar
+** Named-object-sound
+*** Alarm
+*** Buzz
+*** Ka-ching <nowiki>[Cash register sound]</nowiki>
+*** Click
+*** Ding
+*** Horn
+*** Siren
+
+'''Agent-property'''
+* Cognitive-state <nowiki>{requireChild, extensionAllowed}</nowiki>
+** Alert
+** Anesthetized
+** Asleep
+** Attentive
+** Awake
+** Brain-dead
+** Comatose
+** Drowsy
+** Intoxicated <nowiki>[Indicates a state in which participant has consumed alcohol or drugs or other substances that may alter physical or mental state.]</nowiki>
+** Locked-in
+** Passive
+** Resting
+** Vegetative
+* Emotional-state <nowiki>{requireChild, extensionAllowed}</nowiki>
+** Angry
+** Aroused
+** Awed
+** Compassionate
+** Content
+** Disgusted
+** Emotionally-neutral
+** Empathetic
+** Excited
+** Fearful
+** Feeling-stress
+** Frustrated
+** Grieving
+** Happy
+** Jealous
+** Joyful
+** Loving
+** Relieved
+** Sad
+* Postural-state <nowiki>{requireChild}</nowiki>
+** Crouching
+** Kneeling
+** On-treadmill
+** Prone
+** Sitting
+** Standing
+** Supported-by-chin-rest
+* Agent-trait <nowiki>{requireChild, extensionAllowed}</nowiki>
+** Age
+*** <nowiki># {takesValue}</nowiki>
+** Sex
+*** <nowiki># {takesValue}</nowiki>
+** Handedness
+*** <nowiki># {takesValue}</nowiki>
+* Social-role <nowiki>{requireChild} [The role of the agent not particular to the task.]</nowiki>
+** Brother
+** Child
+** Father
+** Mother
+** Sister
+* Task-role <nowiki>{requireChild} [The role of the agent in the task.]</nowiki>
+** Experiment-controller
+** Experiment-subject
+** Experimenter
+** Follower
+** Friend
+** Leader
+** Stranger
+** Student
+** Teacher
+* Association <nowiki>{requireChild}</nowiki>
+** Associated-with-non-agent
+** Associated-with-other <nowiki>[Item such as a cup belonging to another person]</nowiki>
+** Associated-with-self <nowiki>[Item such as a cup belonging to the participant]</nowiki>
+
+'''Data-property'''
+* Statistical-property <nowiki>{requireChild}</nowiki>
+** Accuracy <nowiki>{requireChild}</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Data-mean <nowiki>{requireChild}</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Data-median <nowiki>{requireChild}</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Data-minimum <nowiki>{requireChild}</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Data-maximum <nowiki>{requireChild}</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Statistical-precision <nowiki>{requireChild}</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Statistical-recall <nowiki>{requireChild}</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Standard-deviation <nowiki>{requireChild} [As a Name:Value]</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+* Observational-property <nowiki>{requireChild} [Manually identified data feature. Should be grouped with a label of the form AgentID_featureName]</nowiki>
+** <nowiki># {takesValue}</nowiki>
+* Computed-property <nowiki>{requireChild} [Feature computed by tool. Should be grouped with a label of the form Toolname_propertyName]</nowiki>
+** <nowiki># {takesValue}</nowiki>
+
+'''Task-property'''
+* Attentional-strategy
+** Bottom-up-attention
+** Covert-attention
+** Divided-attention
+** Focused-attention
+** Orienting-attention
+** Overt-attention
+** Selective-attention
+** Sustained-attention
+** Top-down-attention
+* Task-event-type
+** Activity
+** Background-task
+** Experimental-stimulus <nowiki>[Part of something designed to elicit a response in the experiment.]</nowiki>
+** Failure
+** Feedback
+** Incidental
+** Instructional
+** Mishap <nowiki>[Unplanned disruption such as an equipment or experiment control abnormality or experimenter error.]</nowiki>
+** Participant-response <nowiki>[Something related to a participant's actions in performing the task.]</nowiki>
+** Primary-task
+** Warning <nowiki>[As in a warning message that you are getting too close to the shoulder in a driving task]</nowiki>
+* Task-effect-evidence
+** External-evidence <nowiki>[External judgment assumed to be ground truth such as from an experiment control software or an annotator about participant actions such as answering a question, failing to answer in time, etc. Related to participant indication]</nowiki>
+** Intended-effect <nowiki>[How the stimulus is expected to effect the participants based on the experimental design.]</nowiki>
+** Behavioral-evidence
+* Task-response-type
+** Appropriate
+** Correction
+** Erroneous
+** Imagined <nowiki>[This is used to identity that the (sub)event only happened in the imagination of the participant, e.g.  imagined movements in motor imagery paradigms.]</nowiki>
+** Inappropriate
+** Indeterminate
+** Missed
+** Near-miss
+** Successful
+* Task-stimulus-type
+** Cue
+** Distractor
+** Expected <nowiki>[Of low information value, for example frequent Non-targets in an RSVP paradigm]</nowiki>
+** Extraneous
+** Meaningful
+** Newly-learned
+** Non-informative
+** Non-target <nowiki>[Make sure to tag Expected if the Non-target is frequent]</nowiki>
+** Not-meaningful
+** Novel <nowiki>[Genuinely novel such as an event occurring once or so per experiment]</nowiki>
+** Oddball <nowiki>[Unexpected or infrequent]</nowiki>
+** Planned
+** Penalizing
+** Priming
+** Rewarding
+** Target <nowiki>[Something the subject is looking for]</nowiki>
+** Threatening
+** Timed
+** Unexpected
+
+'''Attribute''' <nowiki>{extensionAllowed} [Vocabulary tree for properties.]</nowiki>
+* Informational <nowiki>[Informational properties.]</nowiki>
+** Label <nowiki>{requireChild} [A string of 20 or fewer characters. Labels are used to define scopes or can be assigned to several events sharing the same characteristic (an event code).]</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** ID <nowiki>{requireChild} [An alphanumeric code usually used to identify particular objects.]</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Description <nowiki>{requireChild} [Human-readable text description of the event.]</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Definition <nowiki>[A tag used within a tag group also containing a Label tag to indicate that the Label represents that group of tags.]</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Metadata <nowiki>[A tag used within a tag group indicate to designate information about the data such as file size or author.]</nowiki>
+*** Creation-date <nowiki>{requireChild}</nowiki>
+**** <nowiki># {takesValue, unitClass=datetime}</nowiki>
+*** Library-name <nowiki>{requireChild}</nowiki>
+**** <nowiki># {takesValue}</nowiki>
+*** Subject <nowiki>{requireChild}</nowiki>
+**** <nowiki># {takesValue}</nowiki>
+*** Version <nowiki>{requireChild}</nowiki>
+**** <nowiki># {takesValue}</nowiki>
+** Parameter <nowiki>{requireChild} [As a Name:Value]</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+* Organizational
+** Block <nowiki>[An event used to indicate a particular organizational part in the experimental design or plan. Usually the Block tag is used with scoping tags as well as tags describing what the block represents to assist with downstream analysis.]</nowiki>
+** Collection <nowiki>[A tag designating a grouping of items such as in a set or list.]</nowiki>
+** Context <nowiki>[A tag indicating the overall environment and setup of the experiment or a portion of the experiment. This event is often inserted into a recording at the beginning of an experimental record to describe information such as whether the experiment is conducted indoors or outdoors or whether the experiment is conducted in the real-world or in a controlled laboratory setting. Tags specifying subject metadata might also be included.]</nowiki>
+** Condition-variable <nowiki>[An aspect of the experiment or task that is to be varied during the experiment. Condition-variables are sometimes called independent variables.]</nowiki>
+** Control-variable <nowiki>[An aspect of the experiment that is fixed throughout the study and usually is explicitly controlled.]</nowiki>
+** Data-acquisition <nowiki>[An activity related to data acquisition.]</nowiki>
+** Event-stream <nowiki>[An ordered list of events]</nowiki>
+** Namespace <nowiki>[Tag used to indicate a namespace type of definition.]</nowiki>
+** Permutation <nowiki>[A tag used within a tag group also containing a Label tag to indicate that the Label represents events that are permutations of each other.]</nowiki>
+** Response-variable <nowiki>[An aspect of the experiment or task that is measured as control-variables are varied during the experiment. Response-variables are sometimes called dependent variables.]</nowiki>
+** Sequence <nowiki>[A tag used within a tag group also containing a Label tag to indicate that the Label represents events related in a sequential order.]</nowiki>
+** Session
+** Task <nowiki>[A tag used to indicate a linkage to the task.]</nowiki>
+** Trial <nowiki>[A tag used to indicate a particular organizational part in the experimental design often containing a stimulus-response pair.]</nowiki>
+* Relational <nowiki>{extensionAllowed}</nowiki>
+** Comparative-relation
+*** Less-than
+*** Less-than-or-equal-to
+*** Greater-than
+*** Greater-than-or-equal-to
+*** Equal-to
+*** Not-equal-to
+** Connective-relation
+*** Belongs-to <nowiki>[As in owned by]</nowiki>
+*** Connected-to
+*** Contained-in
+*** Described-by
+*** From-to
+*** Group-of
+*** Implied-by
+*** Interacts-with
+*** Member-of
+*** Part-of
+*** Performed-by
+*** Related-to
+** Spatiotemporal-relation
+*** Directional-relation
+**** Away-from
+**** Backward
+**** Closing
+**** Decreasing
+**** Downward
+**** Forward
+**** Increasing
+**** Leftward
+**** Rightward
+**** Opening
+**** Towards
+**** Upward
+*** Orientational-relation
+**** Horizontally-oriented
+**** Oblique
+**** Rotated
+**** Vertically-oriented
+*** Positional-relation <nowiki>[Should be grouped with what it is positionally related to.]</nowiki>
+**** Above
+**** Across
+**** Adjacent-to
+**** Ahead-of
+**** Around
+**** Back-of
+**** Behind
+**** Below
+**** Between <nowiki>[Should be grouped with the items between designates.]</nowiki>
+**** Bilateral
+**** Bottom-of <nowiki>[At the bottom of something. Should be grouped with the item that it is positionally related to.]</nowiki>
+**** Boundary-of
+**** Center-of <nowiki>[At the center of something. Should be grouped with the item that it is positionally related to.]</nowiki>
+**** Close-to
+**** Distance-from <nowiki>[Should be grouped with an item from which the distance is measured. The resulting group will usually be grouped with an actual Distance or Angle value.]</nowiki>
+**** Edge-of
+**** Far-from
+**** Front-of
+**** Inside-of
+**** Left-of
+**** Left-side
+**** Outside-of
+**** Over
+**** Right-of
+**** Right-side
+**** Top-of
+**** Under
+*** Temporal-relation
+**** After
+**** Asynchronous-with
+**** Before
+**** During
+**** Synchronous-with
+**** Waiting-for
+*** Variability-relation
+**** Constant
+**** Continuous
+**** Deterministic
+**** Discrete
+**** Flickering <nowiki>[Irregular changes in time]</nowiki>
+**** Repetitive
+**** Stochastic
+**** Sudden
+**** Varying
+** Thermal-relation
+*** Cold
+*** Colder-than
+*** Hot
+*** Hotter-than
+* Quantitative <nowiki>{extensionAllowed}</nowiki>
+** Angle <nowiki>{requireChild} [Angle assumed clockwise from vertical unless grouped with axis.]</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Fraction <nowiki>{requireChild} [Fraction of items of a particular type.]</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Interval
+*** <nowiki># {takesValue}</nowiki>
+** Item-count <nowiki>{requireChild}</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Percentage <nowiki>{requireChild} [Percentage of items of a particular type.]</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Probability <nowiki>[Use to specify the level of certainty about the occurrence of the event.]</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Ratio <nowiki>{requireChild}</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Repetition <nowiki>{requireChild} [When the same type of event such as a fixation on the exact same object happens multiple times and it might be necessary to distinguish the first look vs. others]</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Uncertainty <nowiki>{requireChild}</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+* Categorical <nowiki>{extensionAllowed}</nowiki>
+** All
+** Congruent <nowiki>{relatedTag=Incongruent}</nowiki>
+** Constrained <nowiki>{relatedTag=Unconstrained}</nowiki>
+** Correct
+** Deep
+** False
+** High
+** Incongruent
+** Incorrect
+** Invalid
+** Liminal
+** Low
+** Masked <nowiki>[Ability to perceive influenced by presence of another stimulus]</nowiki>
+** Medium
+** None
+** Normal
+** Negative
+** Positive
+** Shallow
+** Some
+** Subliminal
+** Supraliminal
+** Symmetric <nowiki>{relatedTag=Unsymmetric}</nowiki>
+** True
+** Unconstrained <nowiki>{relatedTag=Constrained}</nowiki>
+** Unmasked <nowiki>[Ability to perceive not influenced by presence of another stimulus]</nowiki>
+** Unsymmetric <nowiki>{relatedTag=Symmetric}</nowiki>
+** Valid
+* Descriptive <nowiki>[Indicate the type of thing.]</nowiki>
+** Abstract
+** Biological
+** Clinical
+** Complex
+** Composite
+** Electrical
+** Fractal
+** Involuntary
+** Mechanical
+** Magnetic
+** Numerical
+** Random
+** Symbolic
+* Sensory <nowiki>{extensionAllowed}</nowiki>
+** Auditory
+*** Audible
+*** Frequency <nowiki>{requireChild}</nowiki>
+**** <nowiki># {takesValue, isNumeric, unitClass=frequency}</nowiki>
+*** Loudness <nowiki>{requireChild}</nowiki>
+**** <nowiki># {takesValue, isNumeric}</nowiki>
+*** Monophonic <nowiki>[Consisting of one independent audio channel]</nowiki>
+*** Ramp-up <nowiki>[Increasing in amplitude]</nowiki>
+*** Ramp-down <nowiki>[Decreasing in amplitude]</nowiki>
+*** Silent
+*** Stereophonic <nowiki>[Consisting of more than one independent audio channel]</nowiki>
+*** Timbre <nowiki>[Sound quality]</nowiki>
+** Gustatory <nowiki>[Pertaining to sensations of relating to taste.]</nowiki>
+*** Bitter
+*** Salty
+*** Savory
+*** Sour
+*** Sweet
+** Tactile <nowiki>[Pertaining to sensations of relating to touch.]</nowiki>
+*** Tactile-pressure
+*** Tactile-texture
+*** Tactile-vibration
+** Olfactory <nowiki>[Pertaining to sensations of relating to smell.]</nowiki>
+** Somatic <nowiki>[Pertaining to sensations of nervous system such as pain.]</nowiki>
+*** Pain
+*** Stress
+** Vestibular
+** Visual
+*** Luminance <nowiki>{requireChild}</nowiki>
+*** Color <nowiki>{requireChild}</nowiki>
+**** Color-shade
+***** Dark-shade
+***** Light-shade
+**** HSV-color
+***** Hue
+****** <nowiki># {takesValue, isNumeric} [Angular value between 0 and 360]</nowiki>
+***** Saturation
+****** <nowiki># {takesValue, isNumeric} [B value of RGB between 0 and 1]</nowiki>
+***** HSV-value
+****** <nowiki># {takesValue, isNumeric} [G value of RGB between 0 and 1]</nowiki>
+**** RGB-color
+***** RGB-red
+****** <nowiki># {takesValue, isNumeric} [R value of RGB between 0 and 1]</nowiki>
+***** RGB-blue
+****** <nowiki># {takesValue, isNumeric} [B value of RGB between 0 and 1]</nowiki>
+***** RGB-green
+****** <nowiki># {takesValue, isNumeric} [G value of RGB between 0 and 1]</nowiki>
+**** Grayscale <nowiki>[Indicates gray scale]</nowiki>
+***** <nowiki># {takesValue, isNumeric} [White intensity between 0 and 1]</nowiki>
+**** CSS-color <nowiki>[One of 140 colors supported by all browsers. For more details such as the color RGB or HEX values,  check:  https://www.w3schools.com/colors/colors_groups.asp]</nowiki>
+***** Blue-color
+****** CadetBlue
+****** SteelBlue
+****** LightSteelBlue
+****** LightBlue
+****** PowderBlue
+****** LightSkyBlue
+****** SkyBlue
+****** CornflowerBlue
+****** DeepSkyBlue
+****** DodgerBlue
+****** RoyalBlue
+****** Blue
+****** MediumBlue
+****** DarkBlue
+****** Navy
+****** MidnightBlue
+***** Brown-color
+****** Cornsilk
+****** BlanchedAlmond
+****** Bisque
+****** NavajoWhite
+****** Wheat
+****** BurlyWood
+****** Tan
+****** RosyBrown
+****** SandyBrown
+****** GoldenRod
+****** DarkGoldenRod
+****** Peru
+****** Chocolate
+****** Olive
+****** SaddleBrown
+****** Sienna
+****** Brown
+****** Maroon
+***** Cyan-color
+****** Aqua
+****** Cyan
+****** LightCyan
+****** PaleTurquoise
+****** Aquamarine
+****** Turquoise
+****** MediumTurquoise
+****** DarkTurquoise
+***** Green-color
+****** GreenYellow
+****** Chartreuse
+****** LawnGreen
+****** Lime
+****** LimeGreen
+****** PaleGreen
+****** LightGreen
+****** MediumSpringGreen
+****** SpringGreen
+****** MediumSeaGreen
+****** SeaGreen
+****** ForestGreen
+****** Green
+****** DarkGreen
+****** YellowGreen
+****** OliveDrab
+****** DarkOliveGreen
+****** MediumAquaMarine
+****** DarkSeaGreen
+****** LightSeaGreen
+****** DarkCyan
+****** Teal
+***** Gray-color
+****** Gainsboro
+****** LightGray
+****** Silver
+****** DarkGray
+****** DimGray
+****** Gray
+****** LightSlateGray
+****** SlateGray
+****** DarkSlateGray
+****** Black
+***** Orange-color
+****** Orange
+****** DarkOrange
+****** Coral
+****** Tomato
+****** OrangeRed
+***** Pink-color
+****** Pink
+****** LightPink
+****** HotPink
+****** DeepPink
+****** PaleVioletRed
+****** MediumVioletRed
+***** Purple-color
+****** Lavender
+****** Thistle
+****** Plum
+****** Orchid
+****** Violet
+****** Fuchsia
+****** Magenta
+****** MediumOrchid
+****** DarkOrchid
+****** DarkViolet
+****** BlueViolet
+****** DarkMagenta
+****** Purple
+****** MediumPurple
+****** MediumSlateBlue
+****** SlateBlue
+****** DarkSlateBlue
+****** RebeccaPurple
+****** Indigo
+***** Red-color
+****** LightSalmon
+****** Salmon
+****** DarkSalmon
+****** LightCoral
+****** IndianRed
+****** Crimson
+****** Red
+****** FireBrick
+****** DarkRed
+***** Yellow-color
+****** Gold
+****** Yellow
+****** LightYellow
+****** LemonChiffon
+****** LightGoldenRodYellow
+****** PapayaWhip
+****** Moccasin
+****** PeachPuff
+****** PaleGoldenRod
+****** Khaki
+****** DarkKhaki
+***** White-color
+****** White
+****** Snow
+****** HoneyDew
+****** MintCream
+****** Azure
+****** AliceBlue
+****** GhostWhite
+****** WhiteSmoke
+****** SeaShell
+****** Beige
+****** OldLace
+****** FloralWhite
+****** Ivory
+****** AntiqueWhite
+****** Linen
+****** LavenderBlush
+****** MistyRose
+*** View
+**** 2D-view
+**** 3D-view
+**** Background-view
+**** Bistable-view <nowiki>[Something having two stable visual forms that have two distinguishable stable forms as in optical illusions.]</nowiki>
+**** Foreground-view
+**** Foveal-view <nowiki>[Visual presentation directly on the fovea.]</nowiki>
+**** Map-view <nowiki>[A representation of a geographical location.]</nowiki>
+***** Aerial-view <nowiki>[A representation from an overhead view such as captured by a drone.]</nowiki>
+***** Satellite-view <nowiki>[A representation as captured by technology such as a satellite.]</nowiki>
+***** Street-view <nowiki>[A panoramic view from a position on the ground.]</nowiki>
+**** Peripheral-view
+* Spatiotemporal <nowiki>{extensionAllowed}</nowiki>
+** Spatial
+*** Direction
+**** <nowiki># {takesValue, isNumeric}</nowiki>
+*** Distance <nowiki>{requireChild} [Distance from a specified position in a specified Direction]</nowiki>
+**** <nowiki># {takesValue, isNumeric, unitClass=physicalLength}</nowiki>
+*** Orientation
+**** <nowiki># {takesValue, isNumeric}</nowiki>
+*** Position <nowiki>{requireChild} [Coordinates with respect a specified frame of reference or the default Screen-frame if no frame is given.]</nowiki>
+**** X-position <nowiki>{requireChild}</nowiki>
+***** <nowiki># {takesValue, isNumeric, unitClass=physicalLength}</nowiki>
+**** Y-position <nowiki>{requireChild}</nowiki>
+***** <nowiki># {takesValue, isNumeric, unitClass=physicalLength}</nowiki>
+**** Z-position <nowiki>{requireChild}</nowiki>
+***** <nowiki># {takesValue, isNumeric, unitClass=physicalLength}</nowiki>
+*** Reference-frame <nowiki>[A frame of reference used to express spatial attributes.]</nowiki>
+**** Custom-frame <nowiki>[A custom frame of reference which should be defined in terms of one of the default frames of reference or by a library.]</nowiki>
+**** Room-frame
+**** Screen-frame <nowiki>[HED default frame of reference with origin at center of screen used to display experimental stimuli. Looking out of the screen has +X is horizontal to the right. +Y is up and +Z is out of the front of the screen.]</nowiki>
+**** Screen-facing-frame <nowiki>[HED default frame of reference with origin at the subject Nasion. +X is horizontal to the subject right. +Y is to the subject front and +Z is vertical upward.]</nowiki>
+**** World-frame
+*** Size <nowiki>{requireChild}</nowiki>
+**** Area <nowiki>{requireChild}</nowiki>
+***** <nowiki># {takesValue, isNumeric, unitClass=area}</nowiki>
+**** Length <nowiki>{requireChild} [A measurement of the size along the longest dimension.]</nowiki>
+***** <nowiki># {takesValue, isNumeric, unitClass=physicalLength}</nowiki>
+**** Width <nowiki>[A measurement along the shortest of two or three dimensions.]</nowiki>
+***** <nowiki># {takesValue, isNumeric, unitClass=physicalLength}</nowiki>
+**** Height <nowiki>[A measurement in a direction perpendicular to Length and Width in 3D. In 2D, it is perpendicular to both the Length and Height.]</nowiki>
+***** <nowiki># {takesValue, isNumeric, unitClass=physicalLength}</nowiki>
+**** Volume <nowiki>{requireChild}</nowiki>
+***** <nowiki># {takesValue, isNumeric, unitClass=volume}</nowiki>
+*** Surface-properties
+**** Rough
+**** Smooth
+** Resolution
+*** IO-resolution <nowiki>{requireChild}</nowiki>
+**** Screen-resolution <nowiki>{requireChild} [Number of pixels in each dimension for a digital display device.]</nowiki>
+***** <nowiki># {takesValue}</nowiki>
+**** Printer-resolution <nowiki>{requireChild} [Number of dots-per-inch for a printer.]</nowiki>
+***** <nowiki># {takesValue}</nowiki>
+*** Spatial-resolution <nowiki>{requireChild} [Linear spacing of a spatial measurement.]</nowiki>
+**** <nowiki># {takesValue}</nowiki>
+*** Spectral-resolution <nowiki>{requireChild}</nowiki>
+**** <nowiki># {takesValue}</nowiki>
+*** Temporal-resolution <nowiki>{requireChild}</nowiki>
+**** <nowiki># {takesValue}</nowiki>
+** Temporal
+*** Delay <nowiki>[Indicator of some amount of time elapsed before something happens.]</nowiki>
+**** <nowiki># {takesValue, isNumeric, unitClass=time}</nowiki>
+*** Duration <nowiki>{requireChild} [An offset that is implicit after duration time passed from the onset]</nowiki>
+**** <nowiki># {takesValue, isNumeric, unitClass=time}</nowiki>
+*** Onset
+*** Offset
+*** Pause <nowiki>[Indicating something stopped such as a break or waiting for input.]</nowiki>
+*** Response-start-delay <nowiki>{requireChild} [How long the start of the response is delayed from the stimulus onset.]</nowiki>
+**** <nowiki># {takesValue, isNumeric, unitClass=time}</nowiki>
+*** Response-end-delay <nowiki>{requireChild} [How long the end of the response is delayed from the stimulus onset.]</nowiki>
+**** <nowiki># {takesValue, isNumeric, unitClass=time}</nowiki>
+*** Temporal-value <nowiki>{requireChild}</nowiki>
+**** <nowiki># {takesValue, isNumeric, unitClass=time}</nowiki>
+*** Time-out
+*** Time-sync <nowiki>[Indicates a marker inserted into the recorded data to allow post hoc synchronization of concurrently recorded data streams.]</nowiki>
+** Rate-of-change
+*** Acceleration
+**** <nowiki># {takesValue, isNumeric, unitClass=acceleration}</nowiki>
+*** Jerk-rate <nowiki>[Rate at which an object's acceleration changes with respect to time.]</nowiki>
+**** <nowiki># {takesValue, isNumeric, unitClass=jerk}</nowiki>
+*** Sampling-rate <nowiki>{requireChild} [Number of measurements per unit time.]</nowiki>
+**** <nowiki># {takesValue, unitClass=frequency}</nowiki>
+*** Refresh-rate <nowiki>{requireChild} [Number of times per second a device displays an image. Also known as the frame rate.]</nowiki>
+**** <nowiki># {takesValue, isNumeric}</nowiki>
+*** Temporal-rate <nowiki>[Number of occurrences in a unit of time.]</nowiki>
+**** <nowiki># {takesValue, isNumeric}</nowiki>
+*** Velocity
+**** <nowiki># {takesValue, isNumeric, unitClass=speed}</nowiki>
+* Thermodynamic
+** Energy
+** Pressure
+** Temperature
+*** <nowiki># {takesValue, isNumeric}</nowiki>
+* Environmental <nowiki>{extensionAllowed}</nowiki>
+** Indoors
+** Outdoors
+** Real-world
+** Virtual-world
+** Augmented-reality
+** Motion-platform
+** Urban
+** Rural
+*** Wooden-walkway
+** Terrain
+*** Composite-terrain
+*** Dirt-terrain
+*** Grassy-terrain
+*** Gravel-terrain
+*** Leaf-covered-terrain
+*** Muddy-terrain
+*** Paved-terrain
+*** Rocky-terrain
+*** Sloped-terrain
+*** Uneven-terrain
+* Anatomical
+** Body-part
+*** Head
+**** Hair
+**** Ear
+**** Face
+***** Cheek
+***** Chin
+***** Eye
+***** Eyebrow
+***** Forehead
+***** Lip
+***** Nose
+***** Mouth
+***** Teeth
+*** Arm
+**** Elbow
+**** Hand
+***** Finger
+****** Index-finger
+****** Little-finger
+****** Middle-finger
+****** Ring-finger
+****** Thumb
+**** Wrist
+*** Leg
+**** Ankle
+**** Foot
+***** Toe
+**** Knee
+*** Torso
+**** Upper-torso
+***** Chest
+**** Lower-torso
+***** Hips
+**** Waist
+
+!# end schema
+
+'''Unit classes'''
+* time <nowiki>{defaultUnits=s}</nowiki>
+** second <nowiki>{SIUnit}</nowiki>
+** s <nowiki>{SIUnit, unitSymbol}</nowiki>
+** day
+** minute
+** hour
+* dateTime <nowiki>{defaultUnits=YYYY-MM-DDThh:mm:ss}</nowiki>
+** YYYY-MM-DDThh:mm:ss
+* clockTime <nowiki>{defaultUnits=hour:min}</nowiki>
+** hour:min
+** hour:min:sec
+* frequency <nowiki>{defaultUnits=Hz}</nowiki>
+** hertz <nowiki>{SIUnit}</nowiki>
+** Hz <nowiki>{SIUnit, unitSymbol}</nowiki>
+* angle <nowiki>{defaultUnits=radian}</nowiki>
+** radian <nowiki>{SIUnit}</nowiki>
+** rad <nowiki>{SIUnit, unitSymbol}</nowiki>
+** degree
+* physicalLength <nowiki>{defaultUnits=m}</nowiki>
+** metre <nowiki>{SIUnit}</nowiki>
+** m <nowiki>{SIUnit, unitSymbol}</nowiki>
+** foot
+** mile
+* pixels <nowiki>{defaultUnits=px}</nowiki>
+** pixel
+** px <nowiki>{unitSymbol}</nowiki>
+* area <nowiki>{defaultUnits=m^2}</nowiki>
+** m^2 <nowiki>{SIUnit, unitSymbol}</nowiki>
+** px^2 <nowiki>{unitSymbol}</nowiki>
+** pixel^2
+* volume <nowiki>{defaultUnits=m^3}</nowiki>
+** m^3 <nowiki>{SIUnit, unitSymbol}</nowiki>
+* speed <nowiki>{defaultUnits=m-per-s}</nowiki>
+** m-per-s <nowiki>{SIUnit, unitSymbol}</nowiki>
+** mph <nowiki>{unitSymbol}</nowiki>
+** kph <nowiki>{unitSymbol}</nowiki>
+* acceleration <nowiki>{defaultUnits=m-per-s^2}</nowiki>
+** m-per-s^2 <nowiki>{SIUnit, unitSymbol}</nowiki>
+* jerk <nowiki>{defaultUnits=m-per-s^3}</nowiki>
+** m-per-s^3 <nowiki>{unitSymbol}</nowiki>
+* intensity <nowiki>{defaultUnits=dB}</nowiki>
+** dB <nowiki>{unitSymbol}</nowiki>
+* luminousIntensity <nowiki>{defaultUnits=cd}</nowiki>
+** candela <nowiki>{SIUnit}</nowiki>
+** cd <nowiki>{SIUnit, unitSymbol}</nowiki>
+* memorySize <nowiki>{defaultUnits=B}</nowiki>
+** byte <nowiki>{SIUnit}</nowiki>
+** B <nowiki>{SIUnit, unitSymbol}</nowiki>
+* currency <nowiki>{defaultUnits=$}</nowiki>
+** dollar
+** $ <nowiki>{unitSymbol}</nowiki>
+** point
+** fraction
+
+!# this is not a valid section
+
+'''Unit modifiers'''
+* deca <nowiki>{SIUnitModifier} [SI unit multiple representing 10^1]</nowiki>
+* da <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^1]</nowiki>
+* hecto <nowiki>{SIUnitModifier} [SI unit multiple representing 10^2]</nowiki>
+* h <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^2]</nowiki>
+* kilo <nowiki>{SIUnitModifier} [SI unit multiple representing 10^3]</nowiki>
+* k <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^3]</nowiki>
+* mega <nowiki>{SIUnitModifier} [SI unit multiple representing 10^6]</nowiki>
+* M <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^6]</nowiki>
+* giga <nowiki>{SIUnitModifier} [SI unit multiple representing 10^9]</nowiki>
+* G <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^9]</nowiki>
+* tera <nowiki>{SIUnitModifier} [SI unit multiple representing 10^12]</nowiki>
+* T <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^12]</nowiki>
+* peta <nowiki>{SIUnitModifier} [SI unit multiple representing 10^15]</nowiki>
+* P <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^15]</nowiki>
+* exa <nowiki>{SIUnitModifier} [SI unit multiple representing 10^18]</nowiki>
+* E <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^18]</nowiki>
+* zetta <nowiki>{SIUnitModifier} [SI unit multiple representing 10^21]</nowiki>
+* Z <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^21]</nowiki>
+* yotta <nowiki>{SIUnitModifier} [SI unit multiple representing 10^24]</nowiki>
+* Y <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^24]</nowiki>
+* deci <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-1]</nowiki>
+* d <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-1]</nowiki>
+* centi <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-2]</nowiki>
+* c <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-2]</nowiki>
+* milli <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-3]</nowiki>
+* m <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-3]</nowiki>
+* micro <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-6]</nowiki>
+* u <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-6]</nowiki>
+* nano <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-9]</nowiki>
+* n <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-9]</nowiki>
+* pico <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-12]</nowiki>
+* p <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-12]</nowiki>
+* femto <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-15]</nowiki>
+* f <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-15]</nowiki>
+* atto <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-18]</nowiki>
+* a <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-18]</nowiki>
+* zepto <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-21]</nowiki>
+* z <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-21]</nowiki>
+* yocto <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-24]</nowiki>
+* y <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-24]</nowiki>
+!# end hed
+
+This is an epilogue.
+This is a second line of an epilogue.
\ No newline at end of file
diff --git a/hedtools/tests/schema/test_schema_validator.py b/hedtools/tests/schema/test_check_compliance.py
similarity index 89%
rename from hedtools/tests/schema/test_schema_validator.py
rename to hedtools/tests/schema/test_check_compliance.py
index 20c0bbf0b..2491e18fc 100644
--- a/hedtools/tests/schema/test_schema_validator.py
+++ b/hedtools/tests/schema/test_check_compliance.py
@@ -1,9 +1,11 @@
 import unittest
 import os
-from hed.schema import schema_validator
+from hed.schema import schema_compliance
+from hed import schema
 from hed.util import error_reporter
 from hed.util.error_types import SchemaWarnings
 
+
 class Test(unittest.TestCase):
     # A known schema with many issues.
     schema_file = '../data/HED7.1.1.xml'
@@ -15,16 +17,17 @@ def setUpClass(cls):
 
     def validate_term_base(self, input_text, expected_issues):
         for text, issues in zip(input_text, expected_issues):
-            test_issues = schema_validator.validate_schema_term(text, self.error_handler)
+            test_issues = schema_compliance.validate_schema_term(text, self.error_handler)
             self.assertCountEqual(issues, test_issues)
 
     def validate_desc_base(self, input_descriptions, expected_issues):
         for description, issues in zip(input_descriptions, expected_issues):
-            test_issues = schema_validator.validate_schema_description("dummy", description, self.error_handler)
+            test_issues = schema_compliance.validate_schema_description("dummy", description, self.error_handler)
             self.assertCountEqual(issues, test_issues)
 
     def test_validate_schema(self):
-        issues = schema_validator.validate_schema(self.schema_path)
+        hed_schema = schema.load_schema(self.schema_path)
+        issues = hed_schema.check_compliance()
         self.assertTrue(isinstance(issues, list))
         self.assertTrue(len(issues) > 1)
 
diff --git a/hedtools/tests/schema/test_converters.py b/hedtools/tests/schema/test_converters.py
new file mode 100644
index 000000000..589795e7a
--- /dev/null
+++ b/hedtools/tests/schema/test_converters.py
@@ -0,0 +1,50 @@
+import unittest
+import os
+
+from hed import schema
+
+
+class TestHedSchema(unittest.TestCase):
+    schema_file = '../data/HED7.1.1.xml'
+    schema_file_3g = '../data/HED8.0.0-alpha.1.xml'
+
+    @classmethod
+    def setUpClass(cls):
+        cls.hed_xml = os.path.join(os.path.dirname(os.path.abspath(__file__)), cls.schema_file)
+        cls.hed_schema = schema.load_schema(cls.hed_xml)
+        cls.hed_xml_3g = os.path.join(os.path.dirname(os.path.abspath(__file__)), cls.schema_file_3g)
+        cls.hed_schema_3g = schema.load_schema(cls.hed_xml_3g)
+
+    def test_schema2xml(self):
+        saved_filename = self.hed_schema.save_as_xml()
+        try:
+            loaded_schema = schema.load_schema(saved_filename)
+        finally:
+            os.remove(saved_filename)
+
+        self.assertEqual(loaded_schema, self.hed_schema)
+
+        saved_filename = self.hed_schema_3g.save_as_xml()
+        try:
+            loaded_schema = schema.load_schema(saved_filename)
+        finally:
+            os.remove(saved_filename)
+
+        self.assertEqual(loaded_schema, self.hed_schema_3g)
+
+    def test_schema2wiki(self):
+        saved_filename = self.hed_schema.save_as_mediawiki()
+        try:
+            loaded_schema = schema.load_schema(saved_filename)
+        finally:
+            os.remove(saved_filename)
+
+        self.assertEqual(loaded_schema, self.hed_schema)
+
+        saved_filename = self.hed_schema_3g.save_as_mediawiki()
+        try:
+            loaded_schema = schema.load_schema(saved_filename)
+        finally:
+            os.remove(saved_filename)
+
+        self.assertEqual(loaded_schema, self.hed_schema_3g)
\ No newline at end of file
diff --git a/hedtools/tests/util/test_hed_schema.py b/hedtools/tests/schema/test_hed_schema.py
similarity index 63%
rename from hedtools/tests/util/test_hed_schema.py
rename to hedtools/tests/schema/test_hed_schema.py
index b49b890ac..c92f7e637 100644
--- a/hedtools/tests/util/test_hed_schema.py
+++ b/hedtools/tests/schema/test_hed_schema.py
@@ -1,17 +1,21 @@
 import unittest
 import os
 
-from hed.util.hed_schema import HedSchema
+from hed import schema
+from hed.schema import HedKey
 from hed.util.exceptions import HedFileError
 
 
 class TestHedSchema(unittest.TestCase):
     schema_file = '../data/HED7.1.1.xml'
+    schema_file_3g = '../data/HED8.0.0-alpha.1.xml'
 
     @classmethod
     def setUpClass(cls):
-        hed_xml = os.path.join(os.path.dirname(os.path.abspath(__file__)), cls.schema_file)
-        cls.hed_schema = HedSchema(hed_xml)
+        cls.hed_xml = os.path.join(os.path.dirname(os.path.abspath(__file__)), cls.schema_file)
+        cls.hed_schema = schema.load_schema(cls.hed_xml)
+        cls.hed_xml_3g = os.path.join(os.path.dirname(os.path.abspath(__file__)), cls.schema_file_3g)
+        cls.hed_schema_3g = schema.load_schema(cls.hed_xml_3g)
         cls.hed_schema_dictionaries = cls.hed_schema.dictionaries
 
     def test_invalid_schema(self):
@@ -19,23 +23,23 @@ def test_invalid_schema(self):
         invalid_xml_file = "invalidxmlfile.xml"
         hed_schema = None
         try:
-            hed_schema = HedSchema(invalid_xml_file)
-        except HedFileError as e:
+            hed_schema = schema.load_schema(invalid_xml_file)
+        except HedFileError:
             pass
 
         self.assertFalse(hed_schema)
 
         hed_schema = None
         try:
-            hed_schema = HedSchema(None)
-        except HedFileError as e:
+            hed_schema = schema.load_schema(None)
+        except HedFileError:
             pass
         self.assertFalse(hed_schema)
 
         hed_schema = None
         try:
-            hed_schema = HedSchema("")
-        except HedFileError as e:
+            hed_schema = schema.load_schema("")
+        except HedFileError:
             pass
         self.assertFalse(hed_schema)
 
@@ -43,7 +47,7 @@ def test_display_filename(self):
         invalid_xml_file = "invalidxmlfile.xml"
         display_filename = "PrettyDisplayName.xml"
         try:
-            hed_schema = HedSchema(invalid_xml_file)
+            schema.load_schema(invalid_xml_file)
             # We should have an error before we reach here.
             self.assertTrue(False)
         except HedFileError as e:
@@ -149,6 +153,7 @@ def test_tag_attribute(self):
             'value': {
                 'default': False,
                 'extensionAllowed': False,
+                'extensionAllowedPropagated': False,
                 'isNumeric': True,
                 'position': False,
                 'predicateType': False,
@@ -162,7 +167,8 @@ def test_tag_attribute(self):
             },
             'valueParent': {
                 'default': False,
-                'extensionAllowed': True,
+                'extensionAllowed': False,
+                'extensionAllowedPropagated': True,
                 'isNumeric': False,
                 'position': False,
                 'predicateType': False,
@@ -176,7 +182,8 @@ def test_tag_attribute(self):
             },
             'allowedExtension': {
                 'default': False,
-                'extensionAllowed': True,
+                'extensionAllowed': False,
+                'extensionAllowedPropagated': True,
                 'isNumeric': False,
                 'position': False,
                 'predicateType': False,
@@ -195,12 +202,83 @@ def test_tag_attribute(self):
                 self.assertEqual(self.hed_schema.tag_has_attribute(test_string, attribute), expected_value,
                                  'Test string: %s. Attribute: %s.' % (test_string, attribute))
 
-    def test_get_all_descriptions(self):
-        descriptions = self.hed_schema.get_all_descriptions()
-        self.assertTrue(isinstance(descriptions, dict))
-        self.assertTrue(len(descriptions) > 0)
-
-    def test_get_all_terms(self):
-        terms = self.hed_schema.get_all_terms()
+    def test_get_all_tags(self):
+        terms = self.hed_schema.get_all_tags(True)
         self.assertTrue(isinstance(terms, list))
         self.assertTrue(len(terms) > 0)
+
+    def test_find_duplicate_tags(self):
+        dupe_tags = self.hed_schema.find_duplicate_tags()
+        self.assertEqual(len(dupe_tags), 81)
+
+        dupe_tags = self.hed_schema_3g.find_duplicate_tags()
+        self.assertEqual(len(dupe_tags), 0)
+
+    def test_get_desc_dict(self):
+        desc_dict = self.hed_schema.get_desc_dict()
+        self.assertEqual(len(desc_dict), 358)
+
+        desc_dict = self.hed_schema_3g.get_desc_dict()
+        self.assertEqual(len(desc_dict), 193)
+
+    def test_get_tag_description(self):
+        # Test known tag
+        desc = self.hed_schema.get_tag_description("Event/Category")
+        self.assertEqual(desc, "This is meant to designate the reason this event was recorded")
+        # Test known unit modifier
+        desc = self.hed_schema.get_tag_description("deca", HedKey.SIUnitModifier)
+        self.assertEqual(desc, "SI unit multiple representing 10^1")
+
+        # test unknown tag.
+        desc = self.hed_schema.get_tag_description("This/Is/Not/A/Real/Tag")
+        self.assertEqual(desc, None)
+
+    def test_get_all_tag_attributes(self):
+        tag_props = self.hed_schema.get_all_tag_attributes("Event/Category")
+        expected_props = {
+            "position": "1",
+            "predicateType": "passThrough",
+            "requireChild": "true",
+            "required": "true"
+        }
+        self.assertCountEqual(tag_props, expected_props)
+
+        tag_props = self.hed_schema.get_all_tag_attributes("This/Is/Not/A/Tag")
+        expected_props = {
+        }
+        self.assertCountEqual(tag_props, expected_props)
+
+        tag_props = self.hed_schema_3g.get_all_tag_attributes("Agent-trait")
+        expected_props = {
+            HedKey.ExtensionAllowed: "true",
+            HedKey.RequireChild: "true"
+        }
+        self.assertCountEqual(tag_props, expected_props)
+        # also test long form.
+        tag_props = self.hed_schema_3g.get_all_tag_attributes("Agent-property/Agent-trait")
+        self.assertCountEqual(tag_props, expected_props)
+
+    def test_get_all_forms_of_tag(self):
+        tag_forms = self.hed_schema.get_all_forms_of_tag("Category")
+        expected_forms = []
+        self.assertCountEqual(tag_forms, expected_forms)
+
+        tag_forms = self.hed_schema_3g.get_all_forms_of_tag("Definition")
+        expected_forms = ["definition/", "informational/definition/", "attribute/informational/definition/"]
+        self.assertCountEqual(tag_forms, expected_forms)
+
+        tag_forms = self.hed_schema_3g.get_all_forms_of_tag("This/Is/Not/A/Tag")
+        expected_forms = []
+        self.assertCountEqual(tag_forms, expected_forms)
+
+    def test_get_hed_xml_version(self):
+        self.assertEqual(schema.get_hed_xml_version(self.hed_xml), "7.1.1")
+        self.assertEqual(schema.get_hed_xml_version(self.hed_xml_3g), "8.0.0-alpha.1")
+
+    def test_has_duplicate_tags(self):
+        self.assertTrue(self.hed_schema.has_duplicate_tags())
+        self.assertFalse(self.hed_schema_3g.has_duplicate_tags())
+
+    def test_short_tag_mapping(self):
+        self.assertFalse(self.hed_schema.short_tag_mapping)
+        self.assertEqual(len(self.hed_schema_3g.short_tag_mapping), 986)
diff --git a/hedtools/tests/schema/test_schema_wiki_fatal_errors.py b/hedtools/tests/schema/test_schema_wiki_fatal_errors.py
new file mode 100644
index 000000000..64a1af5d3
--- /dev/null
+++ b/hedtools/tests/schema/test_schema_wiki_fatal_errors.py
@@ -0,0 +1,40 @@
+import unittest
+import os
+
+from hed import schema
+from hed.schema import HedKey
+from hed.util.exceptions import HedFileError, HedExceptions
+
+
+class TestHedSchema(unittest.TestCase):
+    base_schema_dir = '../data/invalid_wiki_schemas/'
+
+    @classmethod
+    def setUpClass(cls):
+        cls.full_base_folder = os.path.join(os.path.dirname(os.path.abspath(__file__)), cls.base_schema_dir)
+        cls.files_and_errors = {
+            "HED_schema_no_start.mediawiki": HedExceptions.SCHEMA_START_MISSING,
+            "HED_schema_no_end.mediawiki": HedExceptions.SCHEMA_END_INVALID,
+            "HED_hed_no_end.mediawiki": HedExceptions.HED_END_INVALID,
+            "HED_separator_invalid.mediawiki": HedExceptions.INVALID_SECTION_SEPARATOR,
+            "HED_header_missing.mediawiki": HedExceptions.SCHEMA_HEADER_MISSING,
+            "HED_header_invalid.mediawiki": HedExceptions.SCHEMA_HEADER_INVALID,
+            "HED_header_invalid_version.mediawiki": HedExceptions.BAD_HED_SEMANTIC_VERSION,
+            "HED_header_missing_version.mediawiki": HedExceptions.BAD_HED_SEMANTIC_VERSION,
+            "HED_header_bad_library.mediawiki": HedExceptions.BAD_HED_LIBRARY_NAME,
+            "HED_schema_out_of_order.mediawiki": HedExceptions.SCHEMA_START_MISSING
+        }
+
+    def test_invalid_schema(self):
+        for filename, error in self.files_and_errors.items():
+            full_filename = self.full_base_folder + filename
+
+            hed_schema = None
+            try:
+                hed_schema = schema.load_schema(full_filename)
+                # all of these should produce exceptions.
+                self.assertFalse(True)
+            except HedFileError as e:
+                formated_error = e.format_error_message()
+                self.assertEqual(e.error_type, error)
+                pass
diff --git a/hedtools/tests/util/test_col_def_group.py b/hedtools/tests/util/test_col_def_group.py
index 036194a0a..e90da52e3 100644
--- a/hedtools/tests/util/test_col_def_group.py
+++ b/hedtools/tests/util/test_col_def_group.py
@@ -4,14 +4,14 @@
 from hed.util.column_def_group import ColumnDefGroup
 from hed.util.column_definition import ColumnDef
 from hed.util.exceptions import HedFileError
-from hed.util.hed_schema import HedSchema
+from hed import schema
 
 class Test(unittest.TestCase):
     @classmethod
     def setUpClass(cls):
         cls.base_data_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), '../data/')
         hed_xml_file = os.path.join(cls.base_data_dir, "HED8.0.0-alpha.1.xml")
-        cls.hed_schema = HedSchema(hed_xml_file)
+        cls.hed_schema = schema.load_schema(hed_xml_file)
         cls.json_filename = os.path.join(cls.base_data_dir, "both_types_events.json")
         cls.default_sidecar = ColumnDefGroup(cls.json_filename)
 
diff --git a/hedtools/tests/util/test_def_dict.py b/hedtools/tests/util/test_def_dict.py
index aa377581c..ca356210b 100644
--- a/hedtools/tests/util/test_def_dict.py
+++ b/hedtools/tests/util/test_def_dict.py
@@ -2,7 +2,7 @@
 import os
 
 from hed.util.def_mapper import DefinitionMapper
-from hed.util.hed_schema import HedSchema
+from hed import schema
 from hed.util.def_dict import DefDict
 
 class Test(unittest.TestCase):
@@ -10,7 +10,7 @@ class Test(unittest.TestCase):
     def setUpClass(cls):
         cls.base_data_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), '../data/')
         hed_xml_file = os.path.join(cls.base_data_dir, "HED8.0.0-alpha.1.xml")
-        cls.hed_schema = HedSchema(hed_xml_file)
+        cls.hed_schema = schema.load_schema(hed_xml_file)
         cls.base_def_mapper = DefinitionMapper(hed_schema=cls.hed_schema)
         cls.def_contents_string = "(Item/TestDef1, Item/TestDef2)"
         cls.basic_def_string = f"(Definition/TestDef, Organizational/TestOrg, {cls.def_contents_string})"
diff --git a/hedtools/tests/util/test_def_mapper.py b/hedtools/tests/util/test_def_mapper.py
index 4dba8de0a..85f615f4c 100644
--- a/hedtools/tests/util/test_def_mapper.py
+++ b/hedtools/tests/util/test_def_mapper.py
@@ -2,7 +2,7 @@
 import os
 
 from hed.util.def_mapper import DefinitionMapper
-from hed.util.hed_schema import HedSchema
+from hed import schema
 from hed.util.def_dict import DefDict
 
 class Test(unittest.TestCase):
@@ -10,7 +10,7 @@ class Test(unittest.TestCase):
     def setUpClass(cls):
         cls.base_data_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), '../data/')
         hed_xml_file = os.path.join(cls.base_data_dir, "HED8.0.0-alpha.1.xml")
-        cls.hed_schema = HedSchema(hed_xml_file)
+        cls.hed_schema = schema.load_schema(hed_xml_file)
         cls.base_dict = DefDict(hed_schema=cls.hed_schema)
         cls.def_contents_string = "(Item/TestDef1, Item/TestDef2)"
         cls.basic_def_string = f"(Definition/TestDef, Organizational/TestOrg, {cls.def_contents_string})"
diff --git a/hedtools/tests/validator/test_tag_validator.py b/hedtools/tests/validator/test_tag_validator.py
index 22a8f2ab1..b2d64865f 100644
--- a/hedtools/tests/validator/test_tag_validator.py
+++ b/hedtools/tests/validator/test_tag_validator.py
@@ -5,7 +5,7 @@
 from hed.validator.hed_validator import HedValidator
 from hed.util import error_reporter
 from hed.validator.tag_validator import TagValidator
-from hed.util.hed_schema import HedSchema
+from hed import schema
 
 
 class TestHed(unittest.TestCase):
@@ -14,7 +14,7 @@ class TestHed(unittest.TestCase):
     @classmethod
     def setUpClass(cls):
         hed_xml = os.path.join(os.path.dirname(os.path.abspath(__file__)), cls.schema_file)
-        cls.hed_schema = HedSchema(hed_xml)
+        cls.hed_schema = schema.load_schema(hed_xml)
         cls.error_handler = error_reporter.ErrorHandler()
         cls.syntactic_tag_validator = TagValidator(check_for_warnings=False,
                                                    run_semantic_validation=False)
@@ -586,7 +586,7 @@ class TestHed3(unittest.TestCase):
     def setUpClass(cls):
         cls.error_handler = error_reporter.ErrorHandler()
         hed_xml = os.path.join(os.path.dirname(os.path.abspath(__file__)), cls.schema_file)
-        cls.hed_schema = HedSchema(hed_xml)
+        cls.hed_schema = schema.load_schema(hed_xml)
         cls.syntactic_tag_validator = TagValidator(cls.hed_schema, check_for_warnings=False,
                                                    run_semantic_validation=False)
         cls.syntactic_warning_tag_validator = TagValidator(cls.hed_schema, check_for_warnings=True,
diff --git a/hedweb/tests/data/HED3.mediawiki b/hedweb/tests/data/HED3.mediawiki
new file mode 100644
index 000000000..1703062bb
--- /dev/null
+++ b/hedweb/tests/data/HED3.mediawiki
@@ -0,0 +1,1166 @@
+HED version: 8.0.0-alpha.1
+
+!# start hed 
+
+'''Event''' 
+* Sensory-event <nowiki>{suggestedTag=Task-property/Task-role}[Something perceivable by the participant. An event meant to be an experimental stimulus should also be tagged with Task-property/Task-role/Experiment-stimulus.]</nowiki>
+* Agent-action <nowiki>[Any action engaged in by an agent. An event that is a participant response to an experiment stimulus should also be tagged with Agent-property/Agent-task-role/Participant-response.]</nowiki>
+* Data-feature <nowiki>[An event derived from the data itself or possibly inserted in a post-hoc fashion. Examples are markers for interictal spikes or alpha bursts.]</nowiki>
+* Experiment-control <nowiki>{extensionAllowed} [An event pertaining to the control of the experiment during its operation.]</nowiki>
+* Experiment-procedure <nowiki>[An event indicating an experimental procedure as in doing a saliva swab on a participant during the experiment or administering a survey.]</nowiki>  
+* Experiment-structure <nowiki>{extensionAllowed} [An event specifying the structure of the experiment.]</nowiki>
+
+
+'''Agent''' <nowiki>[Something in the experiment that takes an active role or produces an effect. The role or effect may be implicit. Being alive or performing an activity such as a computation may qualify something to be an agent. An agent may also be soething that simulates something else.]</nowiki>
+* Animal-agent <nowiki>[An animal that is present.]</nowiki>
+* Avatar-agent
+* Computational-agent
+* Controller-agent <nowiki>[An outside controller such as the experimental control software.]</nowiki> 
+* Human-agent 
+* Robotic-agent 
+
+'''Action''' <nowiki>{extensionAllowed} [May or may not be in response to a previous Sensory presentation event.]</nowiki> 
+* Move
+** Breathe
+*** Inhale <nowiki>[A deliberate in-breath in response to a task instruction or as part of a task. Group with Attribute/Cognitive modifier/Incidental if the inhalation is incidental to the task (Action/Move/Breathe/Inhale, Attribute/Cognitive modifier/Incidental).]</nowiki>
+*** Exhale <nowiki>[A deliberate out-breath in response to a task instruction or as part of a task. Group with Attribute/Cognitive modifier/Incidental: (Action/Move/Breathe/Exhale, Attribute/Cognitive modifier/Incidental).]</nowiki>
+*** Hold-breath
+** Move-full-body
+*** Bend
+*** Dance
+*** Fall-down <nowiki>[Involuntary fall to floor or ground]</nowiki>
+*** Flex
+*** Jerk <nowiki>[When some force applied to the participant causes them to involuntarily accommodate or react to it.]</nowiki>
+***	Recover-balance <nowiki>[A movement made in response to a stumble or unforeseen loss of balance]</nowiki>
+*** Stretch <nowiki>[Stretching the body or body part]</nowiki> 
+*** Shudder 
+*** Stumble <nowiki>[Temporary and involuntary loss of balance]</nowiki>
+*** Turn
+** Move-head
+*** Move-eyes
+**** Blink
+**** Close-eyes
+**** Fixate
+**** Inhibit-blinks
+**** Open-eyes 
+**** Saccade
+*** Move-mouth
+**** Burp 
+**** Clear-throat
+**** Cough 
+**** Gurgle
+**** Hiccup 
+**** Smile
+**** Sneeze 
+**** Sniffle
+**** Swallow
+**** Yawn 
+*** Nod
+*** Shake-head
+** Move-arm-hand
+*** Clap
+*** Drop <nowiki>[For example the participant drops a handheld ball]</nowiki>
+*** Grab
+*** Grasp <nowiki>[Grasp an object in one or both hands]</nowiki>
+*** Hold-down
+*** Lift <nowiki>[Often grouped with item being lifted.]</nowiki>
+*** Make-fist
+*** Point  <nowiki>[Should be grouped with Item/Agent/Human/Body part/Hand/Finger tag to specify which finger was used for the action.]</nowiki> 
+*** Press <nowiki>[Often grouped with body part doing action and item being pressed.]</nowiki>
+*** Push
+*** Reach <nowiki>[Requires a spatial goal such as reaching to touch a button or to grasp something. A body stretch is not a reach.]</nowiki> 
+*** Release <nowiki>[Often grouped with item being released.]</nowiki>
+*** Retract <nowiki>[Often grouped with body part doing action and item being retracted.]</nowiki>
+*** Scratch
+*** Snap-fingers
+*** Tap <nowiki>[Often grouped with body part doing action.]</nowiki>
+*** Touch <nowiki>[May follow a Reach event]</nowiki> 
+** Move-leg-foot
+*** Curl-toes
+*** Jog
+*** Jump
+*** Kick 
+*** Pedal
+*** Run
+*** Step
+**** Heel-strike 
+**** Toe-off
+*** Trot
+*** Walk
+
+* Communicate
+** Communicate-vocally
+*** Cry
+*** Groan
+*** Gulp  <nowiki>[Communicate anxiety. Use swallow if non-editorial.]</nowiki>
+***	Laugh
+*** Scream
+*** Sigh
+*** Speak <nowiki>[Communicate using spoken language.]</nowiki>
+** Gesture <nowiki>[Make a expressive communicative movement of hands/arms and, often, face]</nowiki>
+***	Index-up
+***	Pump-fist
+***	Spread-hand
+***	Shhh
+***	Shrug
+***	Thumb-up
+***	Wave
+** Communicate-musically
+*** Hum
+*** Sing
+*** Vocalize <nowiki>[Make a sustained vowel sound such as Ahhh.]</nowiki>
+*** Whistle
+* Perform <nowiki>[Carry out or accomplish an action or task or function.]</nowiki>
+** Collide
+** Halt <nowiki>[Stop action.]</nowiki>
+** Modify <nowiki>[Deliberately make a change in something.]</nowiki>
+** Operate <nowiki>[Control something else.]</nowiki>
+** Perceive
+*** Feel
+*** Listen
+*** Look
+*** Smell
+*** Taste
+** Play  <nowiki>[As a musical instrument or a game.]</nowiki>
+** Read
+** Rest 
+** Write
+* Think
+** Allow <nowiki>[Allow access to something such as allowing a car to pass]</nowiki> 
+** Attend-to
+** Count 
+** Deny <nowiki> [Deny access to something such as preventing someone to pass]</nowiki> 
+** Detect
+** Discriminate
+** Encode 
+** Evade 
+** Generate
+** Identify
+** Imagine
+** Predict
+** Respond
+** Recall
+** Repeat
+** Switch-attention
+** Track
+
+'''Item''' <nowiki>{extensionAllowed}</nowiki>
+* Language <nowiki>[An  system of communication that is receivable in a sensory presentation.]</nowiki>
+** Character <nowiki>[A mark, sign or symbol used as part of language.]</nowiki>
+** Glyph <nowiki>[A symbol or pictograph.]</nowiki>
+** Nonword <nowiki>[A group of letters or speech sounds that looks or sounds like a word but that is not accepted as such by native speakers.]</nowiki>
+** Phoneme <nowiki>[A sound or a group of different sounds perceived to have the same function by speakers of the language.]</nowiki>
+** Phrase <nowiki>[A small group of words standing together as a conceptual unit.]</nowiki>
+** Sentence <nowiki>[A set of words that is complete in itself.]</nowiki>
+** Syllable <nowiki>[A unit of pronunciation having one vowel sound.]</nowiki>
+** Textblock <nowiki>[A block of text.]</nowiki>
+** Word <nowiki>[A single distinct meaningful element of a language.]</nowiki>
+* Object <nowiki> {extensionAllowed} [Physical object perceptible via Visual, Auditory, Olfactory, and/or Tactile modalities.]</nowiki>
+** Geometric
+*** Pattern
+**** Dots
+**** Led-pattern
+*** 2D-shape
+**** Clockface
+**** Cross
+**** Ellipse
+***** Circle
+**** Rectangle
+***** Square
+**** Single-point
+**** Star
+**** Triangle
+*** 3D-shape
+**** Box
+***** Cube
+**** Cone
+**** Cylinder
+**** Ellipsoid
+***** Sphere
+**** Pyradmid
+** Man-made-object <nowiki>[A manmade object]</nowiki>
+*** Media
+**** Sound-clip
+**** Visualization <nowiki>[A visual representation of something else -- such as using a stick-figure to represent motion capture data.]</nowiki>
+***** Animation
+***** Art-installation
+***** Braille
+***** Cutout
+***** Image <nowiki>[A still 2-D representation.]</nowiki>
+****** Cartoon
+****** Drawing
+****** Icon
+****** Painting
+****** Photograph
+***** Movie
+***** Outline-visualization
+***** Point-light-visualization
+***** Sculpture
+***** Stick-figure-visualization
+*** Building <nowiki>[Whole building or building feature]</nowiki>
+**** Room
+**** Roof
+**** Entrance
+**** Attic
+**** Basement
+*** Clothing
+*** Device
+**** Computing-device
+**** Engine
+**** IO-device
+***** Input-device
+****** Button
+****** Joystick
+****** Keyboard
+******* Key
+******** <nowiki># {takesValue}[Value of key.]</nowiki>
+****** Keypad 
+******* <nowiki># {takesValue}[Value of keypad key.]</nowiki>
+****** Microphone
+****** Mouse
+******* Mouse-button
+******* Scroll-wheel
+****** Touch-screen
+***** Network-device
+***** Output-device
+****** Display-device
+******* Head-mounted-display
+******* LED-display
+******* Screen
+******** Screen-window
+****** Auditory-device
+******* Headphones
+******* Screen-speaker
+******* Loudspeaker
+***** Recording-device
+****** EEG-recording
+****** File-recording
+****** MEG-recording
+****** Motion-capture
+****** Tape-recording
+**** Machine
+***** Noisemaker
+**** Measurement-device
+***** Clock
+****** Clock-face
+**** Robot
+**** Tool
+***	Document <nowiki>[A physical, written document]</nowiki>
+**** Manuscript
+**** Letter
+**** Note
+**** Book
+**** Notebook
+*** Furnishing <nowiki>[Furniture and appliances and other movable accessories including curtains and rugs]</nowiki>
+*** Navigational-object
+**** Path
+**** Road
+***** Lane
+**** Runway
+**** Sign
+*** Vehicle
+**** Aircraft 
+**** Bicycle
+**** Boat 
+**** Car
+**** Cart
+**** Tractor
+**** Train 
+**** Truck 
+** Natural-object <nowiki>[An object occuring naturally in contrast to a man-made object.]</nowiki>
+
+
+*** Mineral <nowiki>[An inorganic substance]</nowiki>
+*** Natural-feature <nowiki>[Some terrain occuring in the outdoors.]</nowiki>
+**** Field
+**** Hill
+**** Mountain
+**** River
+**** Waterfall
+** Food <nowiki>[Something that is usually solid and can be consumed by an organism.]</nowiki>
+** Drink <nowiki>[Something that is liquid and can be consumed by an organism.]</nowiki>
+
+
+* Organism <nowiki>[Something living or part of something living.]</nowiki>
+** Animal <nowiki>[Any multicellular or eukaryotic organism.]</nowiki>
+*** Human
+** Plant
+** Body-part
+*** Head
+**** Hair
+**** Ear
+**** Face
+***** Cheek
+***** Chin
+***** Eye
+***** Eyebrow
+***** Forehead
+***** Lip
+***** Nose
+***** Mouth
+***** Teeth
+*** Arm
+**** Elbow
+**** Hand
+***** Finger
+****** Index-finger
+****** Little-finger
+****** Middle-finger
+****** Ring-finger
+****** Thumb
+**** Wrist
+*** Leg
+**** Ankle
+**** Foot
+***** Toe
+**** Knee
+*** Torso
+**** Upper-torso
+***** Chest
+**** Lower-torso
+***** Hips
+**** Waist
+* Sound <nowiki>[Item/Sound can be used in a tag group to describe a sound created by an action Item/Sound, Action/Move/Move leg-foot/Walk, Agent/Human or an object or Item/Sound, Item/Object/Man-made/Vehicle/Car]</nowiki>
+** Environmental-sound<nowiki>[A background sound from the surrounding environment.]</nowiki>
+*** Crowd-sound <nowiki>[Multiple speech streams at once]</nowiki>
+*** Noise
+**** White-noise
+**** Colored-noise
+** Musical-sound <nowiki>[Any tone or combination of tones with characteristics such as controlled pitch and timbre that can be controlled by an agent.]</nowiki>
+*** Tone
+*** Instrument-sound
+*** Vocalized-sound
+** Named-animal-sound
+*** Bark
+*** Bleat
+*** Crow
+*** Chirp
+*** Growl
+*** Meow
+*** Moo
+*** Purr
+*** Roar
+
+** Named-object-sound
+*** Alarm
+*** Buzz
+*** Ka-ching <nowiki>[Cash register sound]</nowiki>
+*** Click
+*** Ding
+*** Horn
+*** Siren
+
+'''Agent-property''' 
+* Cognitive-state <nowiki>{extensionAllowed, requireChild}</nowiki>
+** Alert
+** Anesthetized 
+** Asleep
+** Attentive
+** Awake 
+** Brain-dead 
+** Comatose 
+** Drowsy 
+** Intoxicated <nowiki>[Indicates a state in which participant has consumed alcohol or drugs or other substances that may alter physical or mental state.]</nowiki> 
+** Locked-in 
+** Passive <nowiki>[Observing but not allowed to make any behavioral outputs. The resting state is not considered passive.</nowiki>
+** Resting
+** Vegetative 
+* Emotional-state <nowiki>{extensionAllowed, requireChild}</nowiki>
+** Angry 
+** Aroused
+** Awed
+** Compassionate
+** Content
+** Disgusted 
+** Emotionally-neutral
+** Empathetic
+** Excited 
+** Fearful
+** Feeling-stress
+** Frustrated
+** Grieving
+** Happy
+** Jealous 
+** Joyful 
+** Loving
+** Relieved
+** Sad 
+* Postural-state <nowiki>[Indicates the position of an agent. Usually this position or posture is static for some length of time rather than being part of a motion.]</nowiki> 
+** Crouching
+** Kneeling
+** On-treadmill
+** Prone
+** Sitting
+** Standing
+** Supported-by-chin-rest
+* Agent-trait <nowiki>{extensionAllowed}</nowiki> 
+** Age
+*** <nowiki># {takesValue}</nowiki>
+** Sex
+*** <nowiki># {takesValue}</nowiki> 
+** Handedness
+*** <nowiki># {takesValue}</nowiki> 
+* Social-role <nowiki> [The role of the agent not particular to the task.]</nowiki>
+** Brother
+** Child
+** Father
+** Mother
+** Sister
+* Agent-task-role <nowiki> [The role of the agent in the task.]</nowiki>
+** Experiment-controller
+** Experiment-participant
+** Experimenter
+** Follower
+** Friend
+** Leader
+** Stranger
+** Student
+** Teacher
+* Association 
+** Associated-with-non-agent 
+** Associated-with-other <nowiki>[Item such as a cup belonging to another person]</nowiki> 
+** Associated-with-self <nowiki>[Item such as a cup belonging to the participant]</nowiki> 
+
+'''Data-property'''
+* Computed-property<nowiki>{extensionAllowed}[Feature computed by tool. Should be grouped with a label of the form Toolname_propertyName]</nowiki>
+** Computed-prediction
+* Data-value
+** <nowiki># {takesValue}</nowiki>
+* Observational-property <nowiki>{extensionAllowed}[Manually identified data feature. Should be grouped with a label of the form AgentID_featureName]</nowiki>
+** Expert-annotation <nowiki>{extensionAllowed}</nowiki>
+** Instrument-measurement <nowiki>{extensionAllowed}</nowiki>
+* Physical-property'''
+** Electrical-property
+** Magnetic-property
+** Mechanical-property
+** Organic-property
+** Thermodynamic-property
+*** Energy
+*** Pressure
+*** Temperature
+**** <nowiki># {takesValue, isNumeric}</nowiki>
+
+* Quantitative <nowiki>{extensionAllowed}</nowiki>
+** Angle<nowiki>[Angle assumed clockwise from vertical unless grouped with axis.]</nowiki>
+*** <nowiki># {takesValue}</nowiki>  
+** Fraction <nowiki>[Fraction of items of a particular type.]</nowiki>
+*** <nowiki># {takesValue}</nowiki> 
+** Interval 
+*** <nowiki># {takesValue}</nowiki>  
+** Item-count 
+*** <nowiki># {takesValue}</nowiki>  
+** Percentage <nowiki>[Percentage of items of a particular type.]</nowiki> 
+*** <nowiki># {takesValue}</nowiki>  
+** Ratio 
+*** <nowiki># {takesValue}</nowiki>  
+** Repetition <nowiki>[When the same type of event such as a fixation on the exact same object happens multiple times and it might be necessary to distinguish the first look vs. others]</nowiki>
+*** <nowiki># {takesValue}</nowiki> 
+* Spatiotemporal <nowiki>{extensionAllowed}</nowiki>
+** Rate-of-change
+*** Acceleration
+**** <nowiki># {takesValue, isNumeric, unitClass=acceleration}</nowiki>  
+*** Jerk-rate <nowiki>[Rate at which the acceleration of an object changes with respect to time.]</nowiki> 
+**** <nowiki># {takesValue, isNumeric, unitClass=jerk}</nowiki>
+*** Sampling-rate <nowiki>{requireChild}[Number of measurements per unit time.]</nowiki> 
+**** <nowiki># {takesValue, unitClass=frequency}</nowiki> 
+*** Refresh-rate <nowiki>{requireChild}[Number of times per second a device displays an image. Also known as the frame rate.]</nowiki> 
+**** <nowiki># {takesValue, isNumeric}</nowiki>
+*** Temporal-rate <nowiki>[Number of occurrences in a unit of time.]</nowiki>
+**** <nowiki># {takesValue, isNumeric}</nowiki> 
+*** Velocity
+**** <nowiki># {takesValue, isNumeric, unitClass=speed}</nowiki> 
+** Resolution
+*** IO-resolution 
+**** Screen-resolution <nowiki>[Number of pixels in each dimension for a digital display device.]</nowiki> 
+***** <nowiki># {takesValue} </nowiki>
+**** Printer-resolution <nowiki>[Number of dots-per-inch for a printer.]</nowiki> 
+***** <nowiki># {takesValue} </nowiki>
+*** Spatial-resolution <nowiki>[Linear spacing of a spatial measurement.]</nowiki> 
+**** <nowiki># {takesValue}</nowiki>
+*** Spectral-resolution 
+**** <nowiki># {takesValue}</nowiki> 
+*** Temporal-resolution 
+**** <nowiki># {takesValue} </nowiki>  
+** Spatial
+*** Direction
+**** <nowiki># {takesValue, isNumeric}</nowiki>
+*** Distance <nowiki>[Distance from a specified position in a specified Direction or grouped with an item from which the distance is measured.]</nowiki> 
+**** <nowiki># {takesValue, isNumeric, unitClass=physicalLength}</nowiki> 
+*** Orientation 
+**** <nowiki># {takesValue, isNumeric}</nowiki> 
+*** Position <nowiki>[Coordinates with respect a specified frame of reference or the default Screen-frame if no frame is given.] </nowiki>
+**** X-position 
+***** <nowiki># {takesValue, isNumeric, unitClass=physicalLength}</nowiki> 
+**** Y-position 
+***** <nowiki># {takesValue, isNumeric, unitClass=physicalLength}</nowiki>  
+**** Z-position 
+***** <nowiki># {takesValue, isNumeric, unitClass=physicalLength}</nowiki>   
+*** Reference-frame <nowiki>[A frame of reference used to express spatial attributes.]</nowiki>
+**** Custom-frame <nowiki><nowiki>[A custom frame of reference which should be defined in terms of one of the default frames of reference or by a library.]</nowiki> 
+**** Room-frame
+**** Screen-frame <nowiki>[HED default frame of reference with origin at center of screen used to display experimental stimuli. Looking out of the screen has +X is horizontal to the right. +Y is up and +Z is out of the front of the screen.]</nowiki>
+**** Screen-facing-frame <nowiki>[HED default frame of reference with origin at the subject Nasion. +X is horizontal to the subject right. +Y is to the subject front and +Z is vertical upward.]</nowiki>
+**** World-frame
+*** Size
+**** Area 
+***** <nowiki># {takesValue, isNumeric, unitClass=area}</nowiki> 
+**** Length <nowiki>[A measurement of the size along the longest dimension.]</nowiki> 
+***** <nowiki># {takesValue, isNumeric, unitClass=physicalLength}</nowiki> 
+**** Width <nowiki>[A measurement along the shortest of two or three dimensions.]</nowiki>
+***** <nowiki># {takesValue, isNumeric, unitClass=physicalLength}</nowiki>
+**** Height <nowiki>[A measurement in a direction perpendicular to Length and Width in 3D. In 2D, it is perpendicular to both the Length and Height.]</nowiki>
+***** <nowiki># {takesValue, isNumeric, unitClass=physicalLength}</nowiki> 
+**** Volume 
+***** <nowiki># {takesValue, isNumeric, unitClass=volume}</nowiki>
+*** Surface-properties
+**** Rough
+**** Smooth
+
+** Temporal
+*** Delay <nowiki>[An indicator that the associated tag group represents something temporally offset from the current event. The delay value can be positive or negative and should always appear in a tag group indicating what is delayed.]</nowiki>
+**** <nowiki># {takesValue, isNumeric, unitClass=time}</nowiki> 
+*** Duration <nowiki>[Time extent of something.]</nowiki>
+**** <nowiki># {takesValue, isNumeric, unitClass=time}</nowiki> 
+*** Onset
+*** Offset
+*** Pause <nowiki>[Indicating something stopped such as a break or waiting for input.]</nowiki>    
+*** Temporal-value 
+**** <nowiki># {takesValue, isNumeric, unitClass=time}</nowiki> 
+*** Time-out
+*** Time-sync <nowiki>[Indicates a marker inserted into the recorded data to allow post hoc synchronization of concurrently recorded data streams.]</nowiki>
+  
+* Statistical-property <nowiki>{extensionAllowed}</nowiki>
+** Accuracy 
+** Data-mean 
+** Data-median 
+** Data-minimum 
+** Data-maximum 
+** Probability <nowiki> [Use to specify the level of certainty about the occurrence of the event.]</nowiki>  
+** Statistical-precision 
+** Statistical-recall 
+** Standard-deviation 
+** Uncertainty <nowiki> [Use to specify the level of error.]</nowiki>
+
+'''Task-property'''
+* Attentional-strategy
+** Bottom-up-attention
+** Covert-attention
+** Divided-attention
+** Focused-attention
+** Orienting-attention
+** Overt-attention
+** Selective-attention
+** Sustained-attention
+** Top-down-attention
+
+* Task-effect-evidence
+** Computational-evidence <nowiki>[Prediction made by computer program.]</nowiki> 
+**  External-evidence <nowiki>[External judgment assumed to be ground truth such as from an experiment control software or an annotator about participant actions such as answering a question, failing to answer in time, etc. Related to participant indication]</nowiki> 
+** Intended-effect <nowiki> [How the stimulus is expected to effect the participants based on the experimental design.]</nowiki> 
+** Behavioral-evidence <nowiki>  [This tag is placed in Agent-action events and grouped  with Task-related tags to specify the type of what that action means with respect to the task. For example if a button press indicates a target then Participant-indication would be grouped with Target.</nowiki>
+
+* Task-role <nowiki>[The purpose with respect to the task.]</nowiki>
+** Activity <nowiki> [Something that is part of the overall task or is necessary to the overall experiment but is not directly part of a stimulus-response cycle. Examples would be taking a survey or provided providing a silva sample.]</nowiki>
+** Background-subtask <nowiki>[A part of the task which should be performed in the background.]</nowiki>
+** Experimental-stimulus <nowiki>[Part of something designed to elicit a response in the experiment.]</nowiki>
+** Failure
+** Incidental <nowiki> [Usually associated with a sensory event intended to give instructions to the participant about the task or behavior.]</nowiki>
+** Instructional <nowiki> [Usually associated with a sensory event intended to give instructions to the participant about the task or behavior.]</nowiki>
+** Mishap <nowiki> [Unplanned disruption such as an equipment or experiment control abnormality or experimenter error.]</nowiki>
+** Participant-response <nowiki> [Something related to a participant actions in performing the task.]</nowiki>
+** Primary-subtask <nowiki>[A part of the task which should be the primary focus of the participant.]</nowiki>
+** Warning <nowiki>[Something that should warn the participant that the parameters of the task have been or are about to be exceeded such as a warning message about getting too close to the shoulder of the road in a driving task.]</nowiki> 
+
+* Task-response-type <nowiki> [How the participant response should be interpreted in terms of the task specification.]</nowiki>
+** Appropriate <nowiki>{relatedTag=Inappropriate}</nowiki>
+** Correction <nowiki>{relatedTag=Successful}</nowiki>
+** Erroneous
+** Imagined <nowiki>[This is used to identity something that only happened in the imagination of the participant as in imagined movements in motor imagery paradigms.]</nowiki>
+** Inappropriate <nowiki>{relatedTag=Appropriate}</nowiki>
+** Indeterminate <nowiki>{relatedTag=Missed,relatedTag=Near-miss}</nowiki>
+** Missed <nowiki>{relatedTag=Indeterminate,relatedTag=Near-miss}</nowiki>
+** Near-miss <nowiki>{relatedTag=Indeterminate,relatedTag=Miss}</nowiki>
+** Successful <nowiki>{relatedTag=Correction}</nowiki>
+
+* Task-stimulus-type <nowiki> [The stimulus purpose in the task.]</nowiki>
+** Cue
+** Distractor 
+** Expected <nowiki>[Of low information value as in frequent non-targets in an RSVP paradigm.]</nowiki>
+** Extraneous
+** Feedback 
+** Meaningful
+** Newly-learned
+** Non-informative
+** Non-target <nowiki>[Make sure to tag Expected if the Non-target is frequent] </nowiki> 
+** Not-meaningful 
+** Novel <nowiki>[Genuinely novel such as an event occurring once or so per experiment]</nowiki> 
+** Oddball <nowiki> [Unexpected or infrequent]</nowiki>
+** Planned
+** Penalty
+** Priming
+** Reward
+** Signal-go
+** Signal-stop 
+** Target <nowiki>[Something the subject is looking for]</nowiki> 
+** Threat
+** Timed
+** Unexpected
+
+
+'''Attribute''' <nowiki>{extensionAllowed} [Subtree tree for general properties.]</nowiki>
+* Informational <nowiki>[Informational properties.]</nowiki>
+** Def <nowiki>{requireChild} [A label that specifically corresponds to a Definition name and indicates that this label represents tags in an unexpanded definition.]</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Defexp <nowiki> {requireChild} [A label that specifically corresponds to a Definition name and indicates that this label is in a group containing an expanded definition and should not be expanded again.]</nowiki>
+*** <nowiki># {takesValue}</nowiki>   
+** Definition <nowiki>{requireChild}[A tag used within a tag group also containing a Label tag to indicate that the Label represents that group of tags.]</nowiki> 
+*** <nowiki># {takesValue}</nowiki>
+** Description <nowiki>{requireChild} [Human-readable text description of the event.]</nowiki> 
+*** <nowiki># {takesValue}</nowiki>
+ 
+** ID <nowiki>{requireChild} [An alphanumeric code usually used to identify a particular instance of an item.]</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Label <nowiki>{requireChild} [A string of 20 or fewer characters identifying something. Labels usually refer to general classes of things while IDs refer to specific instances.]</nowiki>
+*** <nowiki># {takesValue}</nowiki>
+** Metadata <nowiki>[A tag used within a tag group indicate to designate information about the data such as file size or author.]</nowiki>
+*** CogAtlas <nowiki>[A tag used within a tag group indicate a relationship to the CognitiveAtlas terminology.]</nowiki>
+**** <nowiki># {takesValue}</nowiki>
+*** CogPo <nowiki>[A tag used within a tag group indicate a relationship to the CogPO terminology.]</nowiki>
+**** <nowiki># {takesValue}</nowiki>
+*** Creation-date 
+**** <nowiki># {takesValue, unitClass=datetime}</nowiki>
+*** Library-name 
+**** <nowiki># {takesValue}</nowiki>  
+*** Subject 
+**** <nowiki># {takesValue}</nowiki>
+*** Version 
+**** <nowiki># {takesValue}{Usually is a semantic version.]</nowiki> 
+
+** Parameter <nowiki>[As a Name:Value]</nowiki> 
+*** <nowiki># {takesValue}</nowiki>
+
+* Organizational
+** Collection <nowiki>[A tag designating a grouping of items such as in a set or list.]</nowiki> 
+** Event-context <nowiki>[A tag indicating the overall environment and setup of the experiment or a portion of the experiment. This event is often inserted into a recording at the beginning of an experimental record to describe information such as whether the experiment is conducted indoors or outdoors or whether the experiment is conducted in the real-world or in a controlled laboratory setting. Tags specifying subject metadata might also be included. ]</nowiki>
+** Event-stream <nowiki>[An ordered list of events  ]</nowiki>
+** Permutation <nowiki>[A tag used within a tag group also containing a Label tag to indicate that the Label represents events that are permutations of each other.]</nowiki>
+** Recording <nowiki>[Indicates an uninterrupted block of time during which data was acquired using a fixed scanning protocol or sensor positioning. Recording tags are usually have temporal scope which is the entire recording.]</nowiki>
+** Task <nowiki>[A tag used to indicate a linkage the structured activities performed as part of the experiment.]</nowiki> 
+** Task-condition <nowiki>[An aspect of the experiment or task that is to be varied during the experiment. Task-conditions are sometimes called independent variables or contrasts.]</nowiki>
+** Task-control <nowiki>[An aspect of the experiment that is fixed throughout the study and usually is explicitly controlled.]</nowiki>
+** Task-response <nowiki>[An aspect of the experiment or task that is measured as task conditions are varied during the experiment. Task-responses are sometimes called dependent variables.]</nowiki>
+** Task-trial <nowiki>[A tag used to indicate a particular organizational part in the experimental design often containing a stimulus-response pair.]</nowiki>
+** Time-block <nowiki>[A tag used to indicate a contiguous time block in the experiment during which something is fixed or noted.]</nowiki>
+* Relational <nowiki>{extensionAllowed}</nowiki>
+** Comparative-relation
+*** Less-than
+*** Less-than-or-equal-to
+*** Greater-than
+*** Greater-than-or-equal-to
+*** Equal-to
+*** Not-equal-to
+** Connective-relation
+*** Belongs-to <nowiki>[As in owned by]</nowiki>
+*** Connected-to
+*** Contained-in
+*** Described-by
+*** From-to
+*** Group-of
+*** Implied-by
+*** Interacts-with
+*** Member-of
+*** Part-of
+*** Performed-by
+*** Related-to
+
+** Ordering-relation
+*** Early-item <nowiki>[Indication that this item appears early in a sequence.]</nowiki>
+*** First-item <nowiki>[Indication that this is the first item in a sequence.]</nowiki>
+*** Next-item <nowiki>[Indication that this item appears immediately after the previous item in a sequence.]</nowiki> 
+*** Later-item <nowiki>[Indication that this item appears later in a sequence.]</nowiki>
+*** Last-item <nowiki>[Indication that this is the last item in a sequence.]</nowiki>
+
+** Quantitative-relation
+*** Estimated
+*** Approximated
+*** Exact
+
+** Spatiotemporal-relation
+*** Directional-relation
+**** Away-from
+**** Backward
+**** Closing
+**** Decreasing
+**** Downward
+**** Forward
+**** Increasing
+**** Leftward
+**** Rightward
+**** Opening
+**** Towards
+**** Upward
+*** Orientational-relation
+**** Horizontally-oriented
+**** Oblique
+**** Rotated
+**** Vertically-oriented
+*** Positional-relation <nowiki>[Should be grouped with what it is positionally related to.]</nowiki>
+**** Above
+**** Across
+**** Adjacent-to
+**** Ahead-of
+**** Around
+**** Back-of
+**** Behind
+**** Below
+**** Between <nowiki>[Should be grouped with the items between designates.]</nowiki>
+**** Bilateral <nowiki>{Used designate activities or items that occur on both sides or on top and bottom.]</nowiki>
+**** Bottom-of <nowiki>[At the bottom of something. Should be grouped with the item that it is positionally related to.]</nowiki>
+**** Boundary-of
+**** Center-of <nowiki>[At the center of something. Should be grouped with the item that it is positionally related to.]</nowiki>
+**** Close-to
+
+**** Edge-of
+**** Far-from
+**** Front-of
+**** Inside-of
+**** Left-of
+**** Left-side
+**** Outside-of
+**** Over
+**** Right-of
+**** Right-side
+**** Top-of
+**** Under
+*** Temporal-relation
+**** After
+**** Asynchronous-with
+**** Before
+**** During
+**** Synchronous-with
+**** Waiting-for
+*** Variability-relation
+**** Constant
+**** Continuous
+**** Deterministic
+**** Discrete
+**** Flickering <nowiki>[Irregular changes in time.]</nowiki>
+**** Fractal
+**** Random
+**** Repetitive
+**** Sudden
+**** Varying
+
+** Thermal-relation
+*** Cold
+*** Colder-than
+*** Hot
+*** Hotter-than
+
+
+
+* Categorical <nowiki>{extensionAllowed}</nowiki>
+** Abnormal <nowiki>{relatedTag=Normal}</nowiki>
+** All <nowiki>{relatedTag=Some, relatedTag=None}</nowiki>
+** Asymmetric <nowiki>{relatedTag=Symmetric}</nowiki>
+** Congruent <nowiki>{relatedTag=Incongruent}</nowiki>
+** Constrained <nowiki>{relatedTag=Unconstrained}</nowiki>
+** Correct <nowiki>{relatedTag=Incorrect}</nowiki>
+** Deep <nowiki>{relatedTag=Shallow}</nowiki>
+** Disordered <nowiki>{relatedTag=Ordered}</nowiki>
+** False <nowiki>{relatedTag=True}</nowiki>
+** Familiar <nowiki>{relatedTag=Unfamiliar, relatedTag=Famous}[Something known or encountered before.]</nowiki>
+** Famous <nowiki>{relatedTag=Familiar, relatedTag=Unfamiliar}[Something known by many people but necessarily because of direct contact.]</nowiki>
+** High <nowiki>{relatedTag=Low, relatedTag=Medium}</nowiki>
+** Incongruent <nowiki>{relatedTag=Congruent}</nowiki>
+** Incorrect <nowiki>{relatedTag=Correct}</nowiki>
+** Invalid <nowiki>{relatedTag=Valid}</nowiki>
+** Involuntary <nowiki>{relatedTag=Voluntary}</nowiki>
+** Liminal <nowiki>{relatedTag=Subliminal, relatedTag=Supraliminal}</nowiki>
+** Low <nowiki>{relatedTag=High}</nowiki>
+** Masked <nowiki>[Ability to perceive influenced by presence of another stimulus]</nowiki>
+** Medium <nowiki>{relatedTag=Low, relatedTag=High}</nowiki>
+** None <nowiki>{relatedTag=All, relatedTag=Some}</nowiki>
+** Normal <nowiki>{relatedTag=Abnormal}</nowiki>
+** Negative <nowiki>{relatedTag=Positive}</nowiki>
+** Ordered <nowiki>{relatedTag=Disordered}</nowiki>
+** Positive <nowiki>{relatedTag=Negative}</nowiki>
+** Shallow <nowiki>{relatedTag=Deep}</nowiki>
+** Some <nowiki>{relatedTag=All, relatedTag=None}</nowiki>
+** Subliminal <nowiki>{relatedTag=Liminal, relatedTag=Supraliminal}</nowiki>
+** Supraliminal <nowiki>{relatedTag=Liminal, relatedTag=Subliminal}</nowiki>
+** Symmetric <nowiki>{relatedTag=Unsymmetric}</nowiki>
+** True <nowiki>{relatedTag=False}</nowiki>
+** Unconstrained <nowiki>{relatedTag=Constrained}</nowiki>
+** Unfamiliar <nowiki>{relatedTag=Familiar,relatedTag=Famous}[Something not known or encountered before.]</nowiki>
+** Unmasked <nowiki>[Ability to perceive not influenced by presence of another stimulus]</nowiki>
+** Valid <nowiki>{relatedTag=Invalid}</nowiki>
+** Voluntary <nowiki>{relatedTag=Involuntary}</nowiki>
+
+* Descriptive <nowiki>[Indicate the type of thing.]</nowiki>
+** Abstract
+** Clinical
+** Complex
+** Composite
+** Numerical
+** Symbolic
+
+* Sensory <nowiki>{extensionAllowed}</nowiki>
+** Auditory
+*** Audible
+*** Frequency 
+**** <nowiki># {takesValue, isNumeric, unitClass=frequency}</nowiki>  
+*** Loudness 
+**** <nowiki># {takesValue, isNumeric}</nowiki>
+*** Monophonic <nowiki> [Consisting of one independent audio channel]</nowiki>  
+*** Ramp-up <nowiki>[Increasing in amplitude]</nowiki> 
+*** Ramp-down <nowiki>[Decreasing in amplitude]</nowiki>
+*** Silent
+*** Stereophonic <nowiki> [Consisting of more than one independent audio channel]</nowiki> 
+*** Timbre <nowiki>[Sound quality]</nowiki>  
+** Gustatory <nowiki>[Pertaining to sensations of relating to taste.]</nowiki>
+*** Bitter
+*** Salty
+*** Savory
+*** Sour
+*** Sweet
+** Tactile <nowiki>[Pertaining to sensations of relating to touch.]</nowiki>
+*** Tactile-pressure
+*** Tactile-texture
+*** Tactile-vibration
+** Olfactory <nowiki>[Pertaining to sensations of relating to smell.]</nowiki>
+** Somatic <nowiki>[Pertaining to sensations of nervous system such as pain.]</nowiki>
+*** Pain
+*** Stress
+** Vestibular
+** Visual
+*** Luminance 
+*** Color 
+**** Color-shade 
+***** Dark-shade
+***** Light-shade 
+**** HSV-color
+***** Hue 
+****** <nowiki># {takesValue, isNumeric} [Angular value between 0 and 360]</nowiki> 
+***** Saturation 
+****** <nowiki># {takesValue, isNumeric} [B value of RGB between 0 and 1]</nowiki> 
+***** HSV-value 
+****** <nowiki># {takesValue, isNumeric} [G value of RGB between 0 and 1]</nowiki> 
+**** RGB-color
+***** RGB-red 
+****** <nowiki># {takesValue, isNumeric} [R value of RGB between 0 and 1]</nowiki> 
+***** RGB-blue 
+****** <nowiki># {takesValue, isNumeric} [B value of RGB between 0 and 1]</nowiki> 
+***** RGB-green 
+****** <nowiki># {takesValue, isNumeric} [G value of RGB between 0 and 1]</nowiki> 
+**** Grayscale <nowiki>[Indicates gray scale]</nowiki> 
+***** <nowiki># {takesValue, isNumeric} [White intensity between 0 and 1]</nowiki>
+**** CSS-color <nowiki>[One of 140 colors supported by all browsers. For more details such as the color RGB or HEX values,  check:  https://www.w3schools.com/colors/colors_groups.asp]</nowiki> 
+***** Blue-color
+****** CadetBlue
+****** SteelBlue
+****** LightSteelBlue
+****** LightBlue
+****** PowderBlue
+****** LightSkyBlue
+****** SkyBlue
+****** CornflowerBlue
+****** DeepSkyBlue
+****** DodgerBlue
+****** RoyalBlue
+****** Blue
+****** MediumBlue
+****** DarkBlue
+****** Navy
+****** MidnightBlue
+***** Brown-color
+****** Cornsilk 
+****** BlanchedAlmond 
+****** Bisque 
+****** NavajoWhite 
+****** Wheat 
+****** BurlyWood 
+****** Tan 
+****** RosyBrown 
+****** SandyBrown 
+****** GoldenRod 
+****** DarkGoldenRod 
+****** Peru 
+****** Chocolate 
+****** Olive 
+****** SaddleBrown 
+****** Sienna 
+****** Brown 
+****** Maroon 
+***** Cyan-color
+****** Aqua
+****** Cyan
+****** LightCyan
+****** PaleTurquoise
+****** Aquamarine
+****** Turquoise
+****** MediumTurquoise
+****** DarkTurquoise
+***** Green-color
+****** GreenYellow
+****** Chartreuse
+****** LawnGreen
+****** Lime
+****** LimeGreen
+****** PaleGreen
+****** LightGreen
+****** MediumSpringGreen
+****** SpringGreen
+****** MediumSeaGreen
+****** SeaGreen
+****** ForestGreen
+****** Green
+****** DarkGreen
+****** YellowGreen
+****** OliveDrab
+****** DarkOliveGreen
+****** MediumAquaMarine
+****** DarkSeaGreen
+****** LightSeaGreen
+****** DarkCyan
+****** Teal
+***** Gray-color
+****** Gainsboro
+****** LightGray
+****** Silver
+****** DarkGray
+****** DimGray
+****** Gray
+****** LightSlateGray
+****** SlateGray
+****** DarkSlateGray
+****** Black
+***** Orange-color
+****** Orange
+****** DarkOrange
+****** Coral
+****** Tomato
+****** OrangeRed
+***** Pink-color
+****** Pink
+****** LightPink
+****** HotPink
+****** DeepPink
+****** PaleVioletRed
+****** MediumVioletRed
+***** Purple-color
+****** Lavender
+****** Thistle
+****** Plum
+****** Orchid
+****** Violet
+****** Fuchsia
+****** Magenta
+****** MediumOrchid
+****** DarkOrchid
+****** DarkViolet
+****** BlueViolet
+****** DarkMagenta
+****** Purple
+****** MediumPurple
+****** MediumSlateBlue
+****** SlateBlue
+****** DarkSlateBlue
+****** RebeccaPurple
+****** Indigo
+***** Red-color
+****** LightSalmon
+****** Salmon
+****** DarkSalmon
+****** LightCoral
+****** IndianRed
+****** Crimson
+****** Red
+****** FireBrick
+****** DarkRed
+***** Yellow-color
+****** Gold
+****** Yellow
+****** LightYellow
+****** LemonChiffon
+****** LightGoldenRodYellow
+****** PapayaWhip
+****** Moccasin
+****** PeachPuff
+****** PaleGoldenRod
+****** Khaki
+****** DarkKhaki
+***** White-color
+****** White
+****** Snow
+****** HoneyDew
+****** MintCream
+****** Azure
+****** AliceBlue
+****** GhostWhite
+****** WhiteSmoke
+****** SeaShell
+****** Beige
+****** OldLace
+****** FloralWhite
+****** Ivory
+****** AntiqueWhite
+****** Linen
+****** LavenderBlush
+****** MistyRose
+*** View
+**** 2D-view
+**** 3D-view
+**** Background-view
+**** Bistable-view <nowiki>[Something having two stable visual forms that have two distinguishable stable forms as in optical illusions.]</nowiki>
+**** Foreground-view
+**** Foveal-view <nowiki>[Visual presentation directly on the fovea.]</nowiki>
+**** Map-view <nowiki>[A representation of a geographical location.]</nowiki>
+***** Aerial-view <nowiki>[A representation from an overhead view such as captured by a drone.]</nowiki>
+***** Satellite-view <nowiki>[A representation as captured by technology such as a satellite.]</nowiki>
+***** Street-view <nowiki>[A panoramic view from a position on the ground.]</nowiki>
+**** Peripheral-view
+
+
+
+* Environmental <nowiki>{extensionAllowed}</nowiki>
+** Indoors
+** Outdoors
+** Real-world
+** Virtual-world
+** Augmented-reality
+** Motion-platform
+** Urban
+** Rural
+*** Wooden-walkway
+** Terrain
+*** Composite-terrain
+*** Dirt-terrain
+*** Grassy-terrain
+*** Gravel-terrain
+*** Leaf-covered-terrain
+*** Muddy-terrain
+*** Paved-terrain
+*** Rocky-terrain
+*** Sloped-terrain
+*** Uneven-terrain
+
+* Anatomical
+
+
+
+'''Unit classes''' 
+* time <nowiki>{defaultUnits=s}</nowiki> 
+** second <nowiki>{SIUnit}</nowiki> 
+** s <nowiki>{SIUnit, unitSymbol}</nowiki> 
+** day 
+** minute 
+** hour <nowiki>[Should be in 24-hour format.]</nowiki>
+* dateTime <nowiki>{defaultUnits=YYYY-MM-DDThh:mm:ss}</nowiki> 
+** YYYY-MM-DDThh:mm:ss <nowiki>[Date-times should conform to ISO8601 date-time format.]</nowiki> 
+* clockTime <nowiki>{defaultUnits=hour:min}</nowiki> 
+** hour:min <nowiki>[Hours should be in 24 hour format. Two-digit values should be used for each item.]</nowiki> 
+** hour:min:sec <nowiki>[Hours should be in 24 hour format. Two-digit values should be used for each item.]</nowiki> 
+* frequency <nowiki>{defaultUnits=Hz}</nowiki>
+** hertz  <nowiki>{SIUnit}</nowiki>
+** Hz <nowiki>{SIUnit, unitSymbol}</nowiki> 
+* angle <nowiki>{defaultUnits=radian}</nowiki> 
+** radian <nowiki>{SIUnit}</nowiki> 
+** rad <nowiki>{SIUnit, unitSymbol}</nowiki> 
+** degree 
+* physicalLength <nowiki>{defaultUnits=m}</nowiki> 
+** metre <nowiki>{SIUnit}</nowiki> 
+** m <nowiki>{SIUnit, unitSymbol}</nowiki> 
+** foot 
+** mile  
+* pixels <nowiki>{defaultUnits=px}</nowiki>  
+** pixel 
+** px <nowiki>{unitSymbol}</nowiki> 
+* area <nowiki>{defaultUnits=m^2}</nowiki> 
+** m^2 <nowiki>{SIUnit, unitSymbol}</nowiki> 
+** px^2 <nowiki>{unitSymbol}</nowiki> 
+** pixel^2
+* volume <nowiki>{defaultUnits=m^3}</nowiki> 
+** m^3 <nowiki>{SIUnit, unitSymbol}</nowiki> 
+* speed <nowiki>{defaultUnits=m-per-s}</nowiki> 
+** m-per-s <nowiki>{SIUnit, unitSymbol}</nowiki> 
+** mph <nowiki>{unitSymbol}</nowiki> 
+** kph <nowiki>{unitSymbol}</nowiki> 
+* acceleration <nowiki>{defaultUnits=m-per-s^2}</nowiki> 
+** m-per-s^2 <nowiki>{SIUnit, unitSymbol}</nowiki> 
+* jerk <nowiki>{defaultUnits=m-per-s^3}</nowiki> 
+** m-per-s^3 <nowiki>{unitSymbol}</nowiki> 
+* intensity <nowiki>{defaultUnits=dB}</nowiki> 
+** dB <nowiki>{unitSymbol}</nowiki> 
+* luminousIntensity <nowiki>{defaultUnits=cd}</nowiki> 
+** candela <nowiki>{SIUnit}</nowiki> 
+** cd <nowiki>{SIUnit, unitSymbol}</nowiki> 
+* memorySize <nowiki>{defaultUnits=B}</nowiki> 
+** byte <nowiki>{SIUnit}</nowiki>
+** B <nowiki>{SIUnit, unitSymbol}</nowiki> 
+* currency <nowiki>{defaultUnits=$}</nowiki> 
+** dollar 
+** $ <nowiki>{unitSymbol}</nowiki> 
+** point 
+** fraction 
+
+'''Unit modifiers''' 
+* deca <nowiki>{SIUnitModifier} [SI unit multiple representing 10^1]</nowiki> 
+* da <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^1]</nowiki>
+* hecto <nowiki>{SIUnitModifier} [SI unit multiple representing 10^2]</nowiki> 
+* h <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^2]</nowiki>
+* kilo <nowiki>{SIUnitModifier} [SI unit multiple representing 10^3]</nowiki> 
+* k <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^3]</nowiki>
+* mega <nowiki>{SIUnitModifier} [SI unit multiple representing 10^6]</nowiki> 
+* M <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^6]</nowiki>
+* giga <nowiki>{SIUnitModifier} [SI unit multiple representing 10^9]</nowiki> 
+* G <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^9]</nowiki>
+* tera <nowiki>{SIUnitModifier} [SI unit multiple representing 10^12]</nowiki> 
+* T <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^12]</nowiki>
+* peta <nowiki>{SIUnitModifier} [SI unit multiple representing 10^15]</nowiki> 
+* P <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^15]</nowiki> 
+* exa <nowiki>{SIUnitModifier} [SI unit multiple representing 10^18]</nowiki> 
+* E <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^18]</nowiki>
+* zetta <nowiki>{SIUnitModifier} [SI unit multiple representing 10^21]</nowiki> 
+* Z <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^21]</nowiki>
+* yotta <nowiki>{SIUnitModifier} [SI unit multiple representing 10^24]</nowiki> 
+* Y <nowiki>{SIUnitSymbolModifier} [SI unit multiple representing 10^24]</nowiki>   
+* deci <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-1]</nowiki> 
+* d <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-1]</nowiki> 
+* centi <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-2]</nowiki> 
+* c <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-2]</nowiki>
+* milli <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-3]</nowiki> 
+* m <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-3]</nowiki>
+* micro <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-6]</nowiki> 
+* u <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-6]</nowiki>
+* nano <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-9]</nowiki> 
+* n <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-9]</nowiki> 
+* pico <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-12]</nowiki> 
+* p <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-12]</nowiki>
+* femto <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-15]</nowiki> 
+* f <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-15]</nowiki>
+* atto <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-18]</nowiki> 
+* a <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-18]</nowiki>
+* zepto <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-21]</nowiki> 
+* z <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-21]</nowiki>
+* yocto <nowiki>{SIUnitModifier} [SI unit submultiple representing 10^-24]</nowiki> 
+* y <nowiki>{SIUnitSymbolModifier} [SI unit submultiple representing 10^-24]</nowiki>  
+!# end hed 
+
+'''Attribute Definitions:'''
+* defaultUnits <nowiki>[Default units for a tag.]</nowiki> 
+* extensionAllowed <nowiki>[Users can add unlimited levels of child nodes under this tag.]</nowiki> 
+* isNumeric <nowiki>[The tag "#" must be replaced by a numerical value.]</nowiki> 
+* position <nowiki>[Used to specify the order of the required and recommended tags in canonical order for display. The position attribute value should be an integer and the order can start at 0 or 1. Required or recommended tags without this attribute or with negative position will be shown after the others in canonical ordering.]</nowiki> 
+* predicateType <nowiki> [One of propertyOf, subclassOf, passThrough -- used to facilitate mapping to OWL or RDF.]</nowiki>  
+* recommended  <nowiki>[A HED string tagging an event is recommended to include this tag.]</nowiki> 
+* relatedTag <nowiki>[Additional HED tags suggested to be used with this tag. This attribute is used by tagging tools.]</nowiki> 
+* requireChild  <nowiki>[One of its descendants must be chosen to tag an event.]</nowiki> 
+* required <nowiki>[Every HED string tagging an event should include this tag.]</nowiki>
+* SIUnit  <nowiki>[Designates the name of an SI unit so it can be modified by multiple and submultiple names. Note that some units such as byte are designated as SI units although they are not part of the standard.]</nowiki>
+* SIUnitModifier <nowiki>[SI unit modifier indicating a multiple or submultiple of a base unit.]</nowiki> 
+* SIUnitSymbolModifier <nowiki>[SI unit symbol modifier indicating a multiple or submultiple of a base unit symbol.]</nowiki>
+* suggestedTag <nowiki>[Additional HED tags suggested to be used with this tag. This attribute is used by tagging tools.]</nowiki> 
+* takesValue <nowiki>[This tag will have a "#" placeholder child in the schema which is expected to be replaced with a user-defined value.] </nowiki> 
+* unique <nowiki>[Only one of this tag or its descendants can be used within a single tag group or event.]</nowiki> 
+* unitSymbol <nowiki>[Abbreviation or symbol representing a type of unit. Unit symbols  represent both the singular and the plural and thus cannot be pluralized.]</nowiki> 
diff --git a/hedweb/tests/data/HED8.0.0-alpha.1.xml b/hedweb/tests/data/HED8.0.0-alpha.1.xml
index b4cc6d725..d71da44e6 100644
--- a/hedweb/tests/data/HED8.0.0-alpha.1.xml
+++ b/hedweb/tests/data/HED8.0.0-alpha.1.xml
@@ -2,17 +2,17 @@
 <HED version="8.0.0-alpha.1">
    <node>
       <name>Event</name>
-      <node>
+      <node suggestedTag="Task-property/Task-role">
          <name>Sensory-event</name>
-         <description>Something perceivable by the participant. An event meant to be an experimental stimulus should also be tagged with Attribute/Contextual/Task-related. Other events may be tagged with Attribute/Contextual/Non task-related.</description>
+         <description>Something perceivable by the participant. An event meant to be an experimental stimulus should also be tagged with Task-property/Task-role/Experiment-stimulus.</description>
       </node>
       <node>
          <name>Agent-action</name>
-         <description>Any action engaged in by an agent. An event which is a participant response to an experimental stimulus should also be tagged with Attribute/Task-related and Attribute/Role/Experimental subject.</description>
+         <description>Any action engaged in by an agent. An event that is a participant response to an experiment stimulus should also be tagged with Agent-property/Agent-task-role/Participant-response.</description>
       </node>
       <node>
          <name>Data-feature</name>
-         <description>An event derived from the data itself, possibly inserted in a post-hoc fashion, for example an interictal spike or alpha burst.</description>
+         <description>An event derived from the data itself or possibly inserted in a post-hoc fashion. Examples are markers for interictal spikes or alpha bursts.</description>
       </node>
       <node extensionAllowed="true">
          <name>Experiment-control</name>
@@ -20,17 +20,19 @@
       </node>
       <node>
          <name>Experiment-procedure</name>
-         <description>An event indicating an experimental procedure as in doing a saliva swab on the person during the experiment or administering a survey.</description>
+         <description>An event indicating an experimental procedure as in doing a saliva swab on a participant during the experiment or administering a survey.</description>
       </node>
       <node extensionAllowed="true">
          <name>Experiment-structure</name>
          <description>An event specifying the structure of the experiment.</description>
       </node>
    </node>
-   <node extensionAllowed="true">
+   <node>
       <name>Agent</name>
+      <description>Something in the experiment that takes an active role or produces an effect. The role or effect may be implicit. Being alive or performing an activity such as a computation may qualify something to be an agent. An agent may also be soething that simulates something else.</description>
       <node>
          <name>Animal-agent</name>
+         <description>An animal that is present.</description>
       </node>
       <node>
          <name>Avatar-agent</name>
@@ -58,11 +60,11 @@
             <name>Breathe</name>
             <node>
                <name>Inhale</name>
-               <description>A deliberate inbreath, e.g. in response to a task instruction. If incidental, group it with Attribute/Cognitive modifier/Incidental. E.g. (Action/Move/Breathe/Inhale, Attribute/Cognitive modifier/Incidental).</description>
+               <description>A deliberate in-breath in response to a task instruction or as part of a task. Group with Attribute/Cognitive modifier/Incidental if the inhalation is incidental to the task (Action/Move/Breathe/Inhale, Attribute/Cognitive modifier/Incidental).</description>
             </node>
             <node>
                <name>Exhale</name>
-               <description>A deliberate outbreath, e.g. in response to a task instruction. If incidental, group it with Attribute/Cognitive modifier/Incidental. E.g. (Action/Move/Breathe/Exhale, Attribute/Cognitive modifier/Incidental).</description>
+               <description>A deliberate out-breath in response to a task instruction or as part of a task. Group with Attribute/Cognitive modifier/Incidental: (Action/Move/Breathe/Exhale, Attribute/Cognitive modifier/Incidental).</description>
             </node>
             <node>
                <name>Hold-breath</name>
@@ -333,7 +335,7 @@
             </node>
             <node>
                <name>Vocalize</name>
-               <description>Make a sustained vowel sound such as, Ahhh</description>
+               <description>Make a sustained vowel sound such as Ahhh.</description>
             </node>
             <node>
                <name>Whistle</name>
@@ -343,46 +345,52 @@
       <node>
          <name>Perform</name>
          <description>Carry out or accomplish an action or task or function.</description>
-         <node>
-            <name>Appear</name>
-         </node>
-         <node>
-            <name>Change</name>
-         </node>
          <node>
             <name>Collide</name>
          </node>
          <node>
-            <name>Disappear</name>
-         </node>
-         <node>
-            <name>Fill-out-survey</name>
+            <name>Halt</name>
+            <description>Stop action.</description>
          </node>
          <node>
-            <name>Halt</name>
+            <name>Modify</name>
+            <description>Deliberately make a change in something.</description>
          </node>
          <node>
             <name>Operate</name>
             <description>Control something else.</description>
+         </node>
+         <node>
+            <name>Perceive</name>
+            <node>
+               <name>Feel</name>
+            </node>
+            <node>
+               <name>Listen</name>
+            </node>
             <node>
-               <name>Drive</name>
-               <description>Control something that moves.</description>
+               <name>Look</name>
             </node>
             <node>
-               <name>Brake</name>
-               <description>Apply brakes to stop something.</description>
+               <name>Smell</name>
             </node>
             <node>
-               <name>Steer</name>
+               <name>Taste</name>
             </node>
          </node>
          <node>
             <name>Play</name>
             <description>As a musical instrument or a game.</description>
          </node>
+         <node>
+            <name>Read</name>
+         </node>
          <node>
             <name>Rest</name>
          </node>
+         <node>
+            <name>Write</name>
+         </node>
       </node>
       <node>
          <name>Think</name>
@@ -425,7 +433,7 @@
             <name>Predict</name>
          </node>
          <node>
-            <name>Read</name>
+            <name>Respond</name>
          </node>
          <node>
             <name>Recall</name>
@@ -443,6 +451,46 @@
    </node>
    <node extensionAllowed="true">
       <name>Item</name>
+      <node>
+         <name>Language</name>
+         <description>An  system of communication that is receivable in a sensory presentation.</description>
+         <node>
+            <name>Character</name>
+            <description>A mark, sign or symbol used as part of language.</description>
+         </node>
+         <node>
+            <name>Glyph</name>
+            <description>A symbol or pictograph.</description>
+         </node>
+         <node>
+            <name>Nonword</name>
+            <description>A group of letters or speech sounds that looks or sounds like a word but that is not accepted as such by native speakers.</description>
+         </node>
+         <node>
+            <name>Phoneme</name>
+            <description>A sound or a group of different sounds perceived to have the same function by speakers of the language.</description>
+         </node>
+         <node>
+            <name>Phrase</name>
+            <description>A small group of words standing together as a conceptual unit.</description>
+         </node>
+         <node>
+            <name>Sentence</name>
+            <description>A set of words that is complete in itself.</description>
+         </node>
+         <node>
+            <name>Syllable</name>
+            <description>A unit of pronunciation having one vowel sound.</description>
+         </node>
+         <node>
+            <name>Textblock</name>
+            <description>A block of text.</description>
+         </node>
+         <node>
+            <name>Word</name>
+            <description>A single distinct meaningful element of a language.</description>
+         </node>
+      </node>
       <node extensionAllowed="true">
          <name>Object</name>
          <description>Physical object perceptible via Visual, Auditory, Olfactory, and/or Tactile modalities.</description>
@@ -613,17 +661,17 @@
                      </node>
                      <node>
                         <name>Keyboard</name>
-                        <node takesValue="true">
+                        <node>
                            <name>Key</name>
-                           <node>
+                           <node takesValue="true">
                               <name>#</name>
                               <description>Value of key.</description>
                            </node>
                         </node>
                      </node>
-                     <node takesValue="true">
+                     <node>
                         <name>Keypad</name>
-                        <node>
+                        <node takesValue="true">
                            <name>#</name>
                            <description>Value of keypad key.</description>
                         </node>
@@ -789,23 +837,14 @@
          </node>
          <node>
             <name>Natural-object</name>
-            <node>
-               <name>Organism</name>
-               <node>
-                  <name>Animal</name>
-               </node>
-               <node>
-                  <name>Human</name>
-               </node>
-               <node>
-                  <name>Plant</name>
-               </node>
-            </node>
+            <description>An object occuring naturally in contrast to a man-made object.</description>
             <node>
                <name>Mineral</name>
+               <description>An inorganic substance</description>
             </node>
             <node>
                <name>Natural-feature</name>
+               <description>Some terrain occuring in the outdoors.</description>
                <node>
                   <name>Field</name>
                </node>
@@ -825,50 +864,140 @@
          </node>
          <node>
             <name>Food</name>
+            <description>Something that is usually solid and can be consumed by an organism.</description>
          </node>
          <node>
             <name>Drink</name>
+            <description>Something that is liquid and can be consumed by an organism.</description>
          </node>
       </node>
       <node>
-         <name>Language</name>
-         <description>Perceivable in a visual or auditory (spoken) sensory presentation</description>
-         <node>
-            <name>Character</name>
-         </node>
-         <node>
-            <name>Glyph</name>
-         </node>
-         <node>
-            <name>Nonword</name>
-         </node>
-         <node>
-            <name>Phoneme</name>
-         </node>
+         <name>Organism</name>
+         <description>Something living or part of something living.</description>
          <node>
-            <name>Phrase</name>
-         </node>
-         <node>
-            <name>Sentence</name>
-         </node>
-         <node>
-            <name>Syllable</name>
+            <name>Animal</name>
+            <description>Any multicellular or eukaryotic organism.</description>
+            <node>
+               <name>Human</name>
+            </node>
          </node>
          <node>
-            <name>Textblock</name>
+            <name>Plant</name>
          </node>
          <node>
-            <name>Word</name>
+            <name>Body-part</name>
+            <node>
+               <name>Head</name>
+               <node>
+                  <name>Hair</name>
+               </node>
+               <node>
+                  <name>Ear</name>
+               </node>
+               <node>
+                  <name>Face</name>
+                  <node>
+                     <name>Cheek</name>
+                  </node>
+                  <node>
+                     <name>Chin</name>
+                  </node>
+                  <node>
+                     <name>Eye</name>
+                  </node>
+                  <node>
+                     <name>Eyebrow</name>
+                  </node>
+                  <node>
+                     <name>Forehead</name>
+                  </node>
+                  <node>
+                     <name>Lip</name>
+                  </node>
+                  <node>
+                     <name>Nose</name>
+                  </node>
+                  <node>
+                     <name>Mouth</name>
+                  </node>
+                  <node>
+                     <name>Teeth</name>
+                  </node>
+               </node>
+            </node>
+            <node>
+               <name>Arm</name>
+               <node>
+                  <name>Elbow</name>
+               </node>
+               <node>
+                  <name>Hand</name>
+                  <node>
+                     <name>Finger</name>
+                     <node>
+                        <name>Index-finger</name>
+                     </node>
+                     <node>
+                        <name>Little-finger</name>
+                     </node>
+                     <node>
+                        <name>Middle-finger</name>
+                     </node>
+                     <node>
+                        <name>Ring-finger</name>
+                     </node>
+                     <node>
+                        <name>Thumb</name>
+                     </node>
+                  </node>
+               </node>
+               <node>
+                  <name>Wrist</name>
+               </node>
+            </node>
+            <node>
+               <name>Leg</name>
+               <node>
+                  <name>Ankle</name>
+               </node>
+               <node>
+                  <name>Foot</name>
+                  <node>
+                     <name>Toe</name>
+                  </node>
+               </node>
+               <node>
+                  <name>Knee</name>
+               </node>
+            </node>
+            <node>
+               <name>Torso</name>
+               <node>
+                  <name>Upper-torso</name>
+                  <node>
+                     <name>Chest</name>
+                  </node>
+               </node>
+               <node>
+                  <name>Lower-torso</name>
+                  <node>
+                     <name>Hips</name>
+                  </node>
+               </node>
+               <node>
+                  <name>Waist</name>
+               </node>
+            </node>
          </node>
       </node>
       <node>
          <name>Sound</name>
-         <description>Item/Sound can be used in a tag group to describe, for example, sound created by an action Item/Sound, Action/Move/Move leg-foot/Walk, Agent/Human or an object or Item/Sound, Item/Object/Man-made/Vehicle/Car</description>
+         <description>Item/Sound can be used in a tag group to describe a sound created by an action Item/Sound, Action/Move/Move leg-foot/Walk, Agent/Human or an object or Item/Sound, Item/Object/Man-made/Vehicle/Car</description>
          <node>
             <name>Environmental-sound</name>
             <description>A background sound from the surrounding environment.</description>
             <node>
-               <name>Crowd sound</name>
+               <name>Crowd-sound</name>
                <description>Multiple speech streams at once</description>
             </node>
             <node>
@@ -1056,8 +1185,9 @@
             <name>Sad</name>
          </node>
       </node>
-      <node requireChild="true">
+      <node>
          <name>Postural-state</name>
+         <description>Indicates the position of an agent. Usually this position or posture is static for some length of time rather than being part of a motion.</description>
          <node>
             <name>Crouching</name>
          </node>
@@ -1080,7 +1210,7 @@
             <name>Supported-by-chin-rest</name>
          </node>
       </node>
-      <node extensionAllowed="true" requireChild="true">
+      <node extensionAllowed="true">
          <name>Agent-trait</name>
          <node>
             <name>Age</name>
@@ -1101,7 +1231,7 @@
             </node>
          </node>
       </node>
-      <node requireChild="true">
+      <node>
          <name>Social-role</name>
          <description>The role of the agent not particular to the task.</description>
          <node>
@@ -1120,14 +1250,14 @@
             <name>Sister</name>
          </node>
       </node>
-      <node requireChild="true">
-         <name>Task-role</name>
+      <node>
+         <name>Agent-task-role</name>
          <description>The role of the agent in the task.</description>
          <node>
             <name>Experiment-controller</name>
          </node>
          <node>
-            <name>Experiment-subject</name>
+            <name>Experiment-participant</name>
          </node>
          <node>
             <name>Experimenter</name>
@@ -1151,7 +1281,7 @@
             <name>Teacher</name>
          </node>
       </node>
-      <node requireChild="true">
+      <node>
          <name>Association</name>
          <node>
             <name>Associated-with-non-agent</name>
@@ -1168,62 +1298,377 @@
    </node>
    <node>
       <name>Data-property</name>
-      <node requireChild="true">
-         <name>Data-property-value</name>
+      <node extensionAllowed="true">
+         <name>Computed-property</name>
+         <description>Feature computed by tool. Should be grouped with a label of the form Toolname_propertyName</description>
+         <node>
+            <name>Computed-prediction</name>
+         </node>
+      </node>
+      <node>
+         <name>Data-value</name>
          <node takesValue="true">
             <name>#</name>
          </node>
       </node>
-      <node>
-         <name>Statistical-property</name>
-         <node>
-            <name>Accuracy</name>
+      <node extensionAllowed="true">
+         <name>Observational-property</name>
+         <description>Manually identified data feature. Should be grouped with a label of the form AgentID_featureName</description>
+         <node extensionAllowed="true">
+            <name>Expert-annotation</name>
+         </node>
+         <node extensionAllowed="true">
+            <name>Instrument-measurement</name>
          </node>
+      </node>
+      <node>
+         <name>Physical-property</name>
          <node>
-            <name>Data-mean</name>
+            <name>Electrical-property</name>
          </node>
          <node>
-            <name>Data-median</name>
+            <name>Magnetic-property</name>
          </node>
          <node>
-            <name>Data-minimum</name>
+            <name>Mechanical-property</name>
          </node>
          <node>
-            <name>Data-maximum</name>
+            <name>Organic-property</name>
          </node>
          <node>
-            <name>Probability</name>
-            <description>Use to specify the level of certainty about the occurrence of the event.</description>
+            <name>Thermodynamic-property</name>
+            <node>
+               <name>Energy</name>
+            </node>
+            <node>
+               <name>Pressure</name>
+            </node>
+            <node>
+               <name>Temperature</name>
+               <node isNumeric="true" takesValue="true">
+                  <name>#</name>
+               </node>
+            </node>
          </node>
+      </node>
+      <node extensionAllowed="true">
+         <name>Quantitative</name>
          <node>
-            <name>Statistical-precision</name>
+            <name>Angle</name>
+            <description>Angle assumed clockwise from vertical unless grouped with axis.</description>
+            <node takesValue="true">
+               <name>#</name>
+            </node>
          </node>
          <node>
-            <name>Statistical-recall</name>
+            <name>Fraction</name>
+            <description>Fraction of items of a particular type.</description>
+            <node takesValue="true">
+               <name>#</name>
+            </node>
          </node>
          <node>
-            <name>Standard-deviation</name>
+            <name>Interval</name>
+            <node takesValue="true">
+               <name>#</name>
+            </node>
          </node>
          <node>
-            <name>Uncertainty</name>
-            <description>Use to specify the level of error.</description>
+            <name>Item-count</name>
+            <node takesValue="true">
+               <name>#</name>
+            </node>
+         </node>
+         <node>
+            <name>Percentage</name>
+            <description>Percentage of items of a particular type.</description>
+            <node takesValue="true">
+               <name>#</name>
+            </node>
+         </node>
+         <node>
+            <name>Ratio</name>
+            <node takesValue="true">
+               <name>#</name>
+            </node>
+         </node>
+         <node>
+            <name>Repetition</name>
+            <description>When the same type of event such as a fixation on the exact same object happens multiple times and it might be necessary to distinguish the first look vs. others</description>
+            <node takesValue="true">
+               <name>#</name>
+            </node>
          </node>
       </node>
-      <node>
-         <name>Observational-property</name>
-         <description>Manually identified data feature. Should be grouped with a label of the form AgentID_featureName</description>
+      <node extensionAllowed="true">
+         <name>Spatiotemporal</name>
          <node>
-            <name>Expert-annotation</name>
+            <name>Rate-of-change</name>
+            <node>
+               <name>Acceleration</name>
+               <node isNumeric="true" takesValue="true" unitClass="acceleration">
+                  <name>#</name>
+               </node>
+            </node>
+            <node>
+               <name>Jerk-rate</name>
+               <description>Rate at which the acceleration of an object changes with respect to time.</description>
+               <node isNumeric="true" takesValue="true" unitClass="jerk">
+                  <name>#</name>
+               </node>
+            </node>
+            <node requireChild="true">
+               <name>Sampling-rate</name>
+               <description>Number of measurements per unit time.</description>
+               <node takesValue="true" unitClass="frequency">
+                  <name>#</name>
+               </node>
+            </node>
+            <node requireChild="true">
+               <name>Refresh-rate</name>
+               <description>Number of times per second a device displays an image. Also known as the frame rate.</description>
+               <node isNumeric="true" takesValue="true">
+                  <name>#</name>
+               </node>
+            </node>
+            <node>
+               <name>Temporal-rate</name>
+               <description>Number of occurrences in a unit of time.</description>
+               <node isNumeric="true" takesValue="true">
+                  <name>#</name>
+               </node>
+            </node>
+            <node>
+               <name>Velocity</name>
+               <node isNumeric="true" takesValue="true" unitClass="speed">
+                  <name>#</name>
+               </node>
+            </node>
          </node>
          <node>
-            <name>Instrument-measurement</name>
+            <name>Resolution</name>
+            <node>
+               <name>IO-resolution</name>
+               <node>
+                  <name>Screen-resolution</name>
+                  <description>Number of pixels in each dimension for a digital display device.</description>
+                  <node takesValue="true">
+                     <name>#</name>
+                  </node>
+               </node>
+               <node>
+                  <name>Printer-resolution</name>
+                  <description>Number of dots-per-inch for a printer.</description>
+                  <node takesValue="true">
+                     <name>#</name>
+                  </node>
+               </node>
+            </node>
+            <node>
+               <name>Spatial-resolution</name>
+               <description>Linear spacing of a spatial measurement.</description>
+               <node takesValue="true">
+                  <name>#</name>
+               </node>
+            </node>
+            <node>
+               <name>Spectral-resolution</name>
+               <node takesValue="true">
+                  <name>#</name>
+               </node>
+            </node>
+            <node>
+               <name>Temporal-resolution</name>
+               <node takesValue="true">
+                  <name>#</name>
+               </node>
+            </node>
+         </node>
+         <node>
+            <name>Spatial</name>
+            <node>
+               <name>Direction</name>
+               <node isNumeric="true" takesValue="true">
+                  <name>#</name>
+               </node>
+            </node>
+            <node>
+               <name>Distance</name>
+               <description>Distance from a specified position in a specified Direction or grouped with an item from which the distance is measured.</description>
+               <node isNumeric="true" takesValue="true" unitClass="physicalLength">
+                  <name>#</name>
+               </node>
+            </node>
+            <node>
+               <name>Orientation</name>
+               <node isNumeric="true" takesValue="true">
+                  <name>#</name>
+               </node>
+            </node>
+            <node>
+               <name>Position</name>
+               <description>Coordinates with respect a specified frame of reference or the default Screen-frame if no frame is given.</description>
+               <node>
+                  <name>X-position</name>
+                  <node isNumeric="true" takesValue="true" unitClass="physicalLength">
+                     <name>#</name>
+                  </node>
+               </node>
+               <node>
+                  <name>Y-position</name>
+                  <node isNumeric="true" takesValue="true" unitClass="physicalLength">
+                     <name>#</name>
+                  </node>
+               </node>
+               <node>
+                  <name>Z-position</name>
+                  <node isNumeric="true" takesValue="true" unitClass="physicalLength">
+                     <name>#</name>
+                  </node>
+               </node>
+            </node>
+            <node>
+               <name>Reference-frame</name>
+               <description>A frame of reference used to express spatial attributes.</description>
+               <node>
+                  <name>Custom-frame</name>
+                  <description>A custom frame of reference which should be defined in terms of one of the default frames of reference or by a library.</description>
+               </node>
+               <node>
+                  <name>Room-frame</name>
+               </node>
+               <node>
+                  <name>Screen-frame</name>
+                  <description>HED default frame of reference with origin at center of screen used to display experimental stimuli. Looking out of the screen has +X is horizontal to the right. +Y is up and +Z is out of the front of the screen.</description>
+               </node>
+               <node>
+                  <name>Screen-facing-frame</name>
+                  <description>HED default frame of reference with origin at the subject Nasion. +X is horizontal to the subject right. +Y is to the subject front and +Z is vertical upward.</description>
+               </node>
+               <node>
+                  <name>World-frame</name>
+               </node>
+            </node>
+            <node>
+               <name>Size</name>
+               <node>
+                  <name>Area</name>
+                  <node isNumeric="true" takesValue="true" unitClass="area">
+                     <name>#</name>
+                  </node>
+               </node>
+               <node>
+                  <name>Length</name>
+                  <description>A measurement of the size along the longest dimension.</description>
+                  <node isNumeric="true" takesValue="true" unitClass="physicalLength">
+                     <name>#</name>
+                  </node>
+               </node>
+               <node>
+                  <name>Width</name>
+                  <description>A measurement along the shortest of two or three dimensions.</description>
+                  <node isNumeric="true" takesValue="true" unitClass="physicalLength">
+                     <name>#</name>
+                  </node>
+               </node>
+               <node>
+                  <name>Height</name>
+                  <description>A measurement in a direction perpendicular to Length and Width in 3D. In 2D, it is perpendicular to both the Length and Height.</description>
+                  <node isNumeric="true" takesValue="true" unitClass="physicalLength">
+                     <name>#</name>
+                  </node>
+               </node>
+               <node>
+                  <name>Volume</name>
+                  <node isNumeric="true" takesValue="true" unitClass="volume">
+                     <name>#</name>
+                  </node>
+               </node>
+            </node>
+            <node>
+               <name>Surface-properties</name>
+               <node>
+                  <name>Rough</name>
+               </node>
+               <node>
+                  <name>Smooth</name>
+               </node>
+            </node>
+         </node>
+         <node>
+            <name>Temporal</name>
+            <node>
+               <name>Delay</name>
+               <description>An indicator that the associated tag group represents something temporally offset from the current event. The delay value can be positive or negative and should always appear in a tag group indicating what is delayed.</description>
+               <node isNumeric="true" takesValue="true" unitClass="time">
+                  <name>#</name>
+               </node>
+            </node>
+            <node>
+               <name>Duration</name>
+               <description>Time extent of something.</description>
+               <node isNumeric="true" takesValue="true" unitClass="time">
+                  <name>#</name>
+               </node>
+            </node>
+            <node>
+               <name>Onset</name>
+            </node>
+            <node>
+               <name>Offset</name>
+            </node>
+            <node>
+               <name>Pause</name>
+               <description>Indicating something stopped such as a break or waiting for input.</description>
+            </node>
+            <node>
+               <name>Temporal-value</name>
+               <node isNumeric="true" takesValue="true" unitClass="time">
+                  <name>#</name>
+               </node>
+            </node>
+            <node>
+               <name>Time-out</name>
+            </node>
+            <node>
+               <name>Time-sync</name>
+               <description>Indicates a marker inserted into the recorded data to allow post hoc synchronization of concurrently recorded data streams.</description>
+            </node>
          </node>
       </node>
-      <node>
-         <name>Computed-property</name>
-         <description>Feature computed by tool. Should be grouped with a label of the form Toolname_propertyName</description>
+      <node extensionAllowed="true">
+         <name>Statistical-property</name>
          <node>
-            <name>Computed-prediction</name>
+            <name>Accuracy</name>
+         </node>
+         <node>
+            <name>Data-mean</name>
+         </node>
+         <node>
+            <name>Data-median</name>
+         </node>
+         <node>
+            <name>Data-minimum</name>
+         </node>
+         <node>
+            <name>Data-maximum</name>
+         </node>
+         <node>
+            <name>Probability</name>
+            <description>Use to specify the level of certainty about the occurrence of the event.</description>
+         </node>
+         <node>
+            <name>Statistical-precision</name>
+         </node>
+         <node>
+            <name>Statistical-recall</name>
+         </node>
+         <node>
+            <name>Standard-deviation</name>
+         </node>
+         <node>
+            <name>Uncertainty</name>
+            <description>Use to specify the level of error.</description>
          </node>
       </node>
    </node>
@@ -1278,12 +1723,15 @@
          </node>
       </node>
       <node>
-         <name>Task-event-type</name>
+         <name>Task-role</name>
+         <description>The purpose with respect to the task.</description>
          <node>
             <name>Activity</name>
+            <description>Something that is part of the overall task or is necessary to the overall experiment but is not directly part of a stimulus-response cycle. Examples would be taking a survey or provided providing a silva sample.</description>
          </node>
          <node>
-            <name>Background-task</name>
+            <name>Background-subtask</name>
+            <description>A part of the task which should be performed in the background.</description>
          </node>
          <node>
             <name>Experimental-stimulus</name>
@@ -1292,14 +1740,13 @@
          <node>
             <name>Failure</name>
          </node>
-         <node>
-            <name>Feedback</name>
-         </node>
          <node>
             <name>Incidental</name>
+            <description>Usually associated with a sensory event intended to give instructions to the participant about the task or behavior.</description>
          </node>
          <node>
             <name>Instructional</name>
+            <description>Usually associated with a sensory event intended to give instructions to the participant about the task or behavior.</description>
          </node>
          <node>
             <name>Mishap</name>
@@ -1307,22 +1754,24 @@
          </node>
          <node>
             <name>Participant-response</name>
-            <description>Something related to a participant's actions in performing the task.</description>
+            <description>Something related to a participant actions in performing the task.</description>
          </node>
          <node>
-            <name>Primary-task</name>
+            <name>Primary-subtask</name>
+            <description>A part of the task which should be the primary focus of the participant.</description>
          </node>
          <node>
             <name>Warning</name>
-            <description>As in a warning message that you are getting too close to the shoulder in a driving task</description>
+            <description>Something that should warn the participant that the parameters of the task have been or are about to be exceeded such as a warning message about getting too close to the shoulder of the road in a driving task.</description>
          </node>
       </node>
       <node>
          <name>Task-response-type</name>
-         <node>
+         <description>How the participant response should be interpreted in terms of the task specification.</description>
+         <node relatedTag="Inappropriate">
             <name>Appropriate</name>
          </node>
-         <node>
+         <node relatedTag="Successful">
             <name>Correction</name>
          </node>
          <node>
@@ -1330,26 +1779,27 @@
          </node>
          <node>
             <name>Imagined</name>
-            <description>This is used to identity that the (sub)event only happened in the imagination of the participant, e.g.  imagined movements in motor imagery paradigms.</description>
+            <description>This is used to identity something that only happened in the imagination of the participant as in imagined movements in motor imagery paradigms.</description>
          </node>
-         <node>
+         <node relatedTag="Appropriate">
             <name>Inappropriate</name>
          </node>
-         <node>
+         <node relatedTag="Near-miss">
             <name>Indeterminate</name>
          </node>
-         <node>
+         <node relatedTag="Near-miss">
             <name>Missed</name>
          </node>
-         <node>
+         <node relatedTag="Miss">
             <name>Near-miss</name>
          </node>
-         <node>
+         <node relatedTag="Correction">
             <name>Successful</name>
          </node>
       </node>
       <node>
          <name>Task-stimulus-type</name>
+         <description>The stimulus purpose in the task.</description>
          <node>
             <name>Cue</name>
          </node>
@@ -1358,11 +1808,14 @@
          </node>
          <node>
             <name>Expected</name>
-            <description>Of low information value, for example frequent Non-targets in an RSVP paradigm</description>
+            <description>Of low information value as in frequent non-targets in an RSVP paradigm.</description>
          </node>
          <node>
             <name>Extraneous</name>
          </node>
+         <node>
+            <name>Feedback</name>
+         </node>
          <node>
             <name>Meaningful</name>
          </node>
@@ -1391,13 +1844,13 @@
             <name>Planned</name>
          </node>
          <node>
-            <name>Penalizing</name>
+            <name>Penalty</name>
          </node>
          <node>
             <name>Priming</name>
          </node>
          <node>
-            <name>Rewarding</name>
+            <name>Reward</name>
          </node>
          <node>
             <name>Signal-go</name>
@@ -1410,7 +1863,7 @@
             <description>Something the subject is looking for</description>
          </node>
          <node>
-            <name>Threatening</name>
+            <name>Threat</name>
          </node>
          <node>
             <name>Timed</name>
@@ -1422,48 +1875,48 @@
    </node>
    <node extensionAllowed="true">
       <name>Attribute</name>
-      <description>Vocabulary tree for properties.</description>
+      <description>Subtree tree for general properties.</description>
       <node>
          <name>Informational</name>
          <description>Informational properties.</description>
          <node requireChild="true">
-            <name>Label</name>
-            <description>A string of 20 or fewer characters identifying something. Labels usually refer to general classes of things while IDs refer to specific instances.</description>
+            <name>Def</name>
+            <description>A label that specifically corresponds to a Definition name and indicates that this label represents tags in an unexpanded definition.</description>
             <node takesValue="true">
                <name>#</name>
             </node>
          </node>
          <node requireChild="true">
-            <name>Label-def</name>
-            <description>A label that specifically corresponds to a Definition name and indicates that this label represents tags in an unexpanded definition.</description>
+            <name>Defexp</name>
+            <description>A label that specifically corresponds to a Definition name and indicates that this label is in a group containing an expanded definition and should not be expanded again.</description>
             <node takesValue="true">
                <name>#</name>
             </node>
          </node>
          <node requireChild="true">
-            <name>Label-exp</name>
-            <description>A label that specifically corresponds to a Definition name and indicates that this label is in a group containing an expanded definition.</description>
+            <name>Definition</name>
+            <description>A tag used within a tag group also containing a Label tag to indicate that the Label represents that group of tags.</description>
             <node takesValue="true">
                <name>#</name>
             </node>
          </node>
          <node requireChild="true">
-            <name>ID</name>
-            <description>An alphanumeric code usually used to identify a particular instance of an item.</description>
+            <name>Description</name>
+            <description>Human-readable text description of the event.</description>
             <node takesValue="true">
                <name>#</name>
             </node>
          </node>
          <node requireChild="true">
-            <name>Description</name>
-            <description>Human-readable text description of the event.</description>
+            <name>ID</name>
+            <description>An alphanumeric code usually used to identify a particular instance of an item.</description>
             <node takesValue="true">
                <name>#</name>
             </node>
          </node>
-         <node>
-            <name>Definition</name>
-            <description>A tag used within a tag group also containing a Label tag to indicate that the Label represents that group of tags.</description>
+         <node requireChild="true">
+            <name>Label</name>
+            <description>A string of 20 or fewer characters identifying something. Labels usually refer to general classes of things while IDs refer to specific instances.</description>
             <node takesValue="true">
                <name>#</name>
             </node>
@@ -1471,32 +1924,46 @@
          <node>
             <name>Metadata</name>
             <description>A tag used within a tag group indicate to designate information about the data such as file size or author.</description>
-            <node requireChild="true">
+            <node>
+               <name>CogAtlas</name>
+               <description>A tag used within a tag group indicate a relationship to the CognitiveAtlas terminology.</description>
+               <node takesValue="true">
+                  <name>#</name>
+               </node>
+            </node>
+            <node>
+               <name>CogPo</name>
+               <description>A tag used within a tag group indicate a relationship to the CogPO terminology.</description>
+               <node takesValue="true">
+                  <name>#</name>
+               </node>
+            </node>
+            <node>
                <name>Creation-date</name>
                <node takesValue="true" unitClass="datetime">
                   <name>#</name>
                </node>
             </node>
-            <node requireChild="true">
+            <node>
                <name>Library-name</name>
                <node takesValue="true">
                   <name>#</name>
                </node>
             </node>
-            <node requireChild="true">
+            <node>
                <name>Subject</name>
                <node takesValue="true">
                   <name>#</name>
                </node>
             </node>
-            <node requireChild="true">
+            <node>
                <name>Version</name>
                <node takesValue="true">
                   <name>#</name>
                </node>
             </node>
          </node>
-         <node requireChild="true">
+         <node>
             <name>Parameter</name>
             <description>As a Name:Value</description>
             <node takesValue="true">
@@ -1506,61 +1973,50 @@
       </node>
       <node>
          <name>Organizational</name>
-         <node>
-            <name>Block</name>
-            <description>An event used to indicate a particular organizational part in the experimental design or plan. Usually the Block tag is used with scoping tags as well as tags describing what the block represents to assist with downstream analysis.</description>
-         </node>
          <node>
             <name>Collection</name>
             <description>A tag designating a grouping of items such as in a set or list.</description>
          </node>
          <node>
-            <name>Context</name>
+            <name>Event-context</name>
             <description>A tag indicating the overall environment and setup of the experiment or a portion of the experiment. This event is often inserted into a recording at the beginning of an experimental record to describe information such as whether the experiment is conducted indoors or outdoors or whether the experiment is conducted in the real-world or in a controlled laboratory setting. Tags specifying subject metadata might also be included.</description>
          </node>
-         <node>
-            <name>Condition-variable</name>
-            <description>An aspect of the experiment or task that is to be varied during the experiment. Condition-variables are sometimes called independent variables.</description>
-         </node>
-         <node>
-            <name>Control-variable</name>
-            <description>An aspect of the experiment that is fixed throughout the study and usually is explicitly controlled.</description>
-         </node>
-         <node>
-            <name>Data-acquisition</name>
-            <description>An activity related to data acquisition.</description>
-         </node>
          <node>
             <name>Event-stream</name>
             <description>An ordered list of events</description>
          </node>
-         <node>
-            <name>Namespace</name>
-            <description>Tag used to indicate a namespace type of definition.</description>
-         </node>
          <node>
             <name>Permutation</name>
             <description>A tag used within a tag group also containing a Label tag to indicate that the Label represents events that are permutations of each other.</description>
          </node>
          <node>
-            <name>Response-variable</name>
-            <description>An aspect of the experiment or task that is measured as control-variables are varied during the experiment. Response-variables are sometimes called dependent variables.</description>
+            <name>Recording</name>
+            <description>Indicates an uninterrupted block of time during which data was acquired using a fixed scanning protocol or sensor positioning. Recording tags are usually have temporal scope which is the entire recording.</description>
          </node>
          <node>
-            <name>Sequence</name>
-            <description>A tag used within a tag group also containing a Label tag to indicate that the Label represents events related in a sequential order.</description>
+            <name>Task</name>
+            <description>A tag used to indicate a linkage the structured activities performed as part of the experiment.</description>
          </node>
          <node>
-            <name>Session</name>
+            <name>Task-condition</name>
+            <description>An aspect of the experiment or task that is to be varied during the experiment. Task-conditions are sometimes called independent variables or contrasts.</description>
          </node>
          <node>
-            <name>Task</name>
-            <description>A tag used to indicate a linkage to the task.</description>
+            <name>Task-control</name>
+            <description>An aspect of the experiment that is fixed throughout the study and usually is explicitly controlled.</description>
          </node>
          <node>
-            <name>Trial</name>
+            <name>Task-response</name>
+            <description>An aspect of the experiment or task that is measured as task conditions are varied during the experiment. Task-responses are sometimes called dependent variables.</description>
+         </node>
+         <node>
+            <name>Task-trial</name>
             <description>A tag used to indicate a particular organizational part in the experimental design often containing a stimulus-response pair.</description>
          </node>
+         <node>
+            <name>Time-block</name>
+            <description>A tag used to indicate a contiguous time block in the experiment during which something is fixed or noted.</description>
+         </node>
       </node>
       <node extensionAllowed="true">
          <name>Relational</name>
@@ -1838,7 +2294,7 @@
                </node>
                <node>
                   <name>Flickering</name>
-                  <description>Irregular changes in time</description>
+                  <description>Irregular changes in time.</description>
                </node>
                <node>
                   <name>Fractal</name>
@@ -1854,71 +2310,22 @@
                </node>
                <node>
                   <name>Varying</name>
-               </node>
-            </node>
-         </node>
-         <node>
-            <name>Thermal-relation</name>
-            <node>
-               <name>Cold</name>
-            </node>
-            <node>
-               <name>Colder-than</name>
-            </node>
-            <node>
-               <name>Hot</name>
-            </node>
-            <node>
-               <name>Hotter-than</name>
-            </node>
-         </node>
-      </node>
-      <node extensionAllowed="true">
-         <name>Quantitative</name>
-         <node requireChild="true">
-            <name>Angle</name>
-            <description>Angle assumed clockwise from vertical unless grouped with axis.</description>
-            <node takesValue="true">
-               <name>#</name>
-            </node>
-         </node>
-         <node requireChild="true">
-            <name>Fraction</name>
-            <description>Fraction of items of a particular type.</description>
-            <node takesValue="true">
-               <name>#</name>
+               </node>
             </node>
          </node>
          <node>
-            <name>Interval</name>
-            <node takesValue="true">
-               <name>#</name>
-            </node>
-         </node>
-         <node requireChild="true">
-            <name>Item-count</name>
-            <node takesValue="true">
-               <name>#</name>
+            <name>Thermal-relation</name>
+            <node>
+               <name>Cold</name>
             </node>
-         </node>
-         <node requireChild="true">
-            <name>Percentage</name>
-            <description>Percentage of items of a particular type.</description>
-            <node takesValue="true">
-               <name>#</name>
+            <node>
+               <name>Colder-than</name>
             </node>
-         </node>
-         <node requireChild="true">
-            <name>Ratio</name>
-            <node takesValue="true">
-               <name>#</name>
+            <node>
+               <name>Hot</name>
             </node>
-         </node>
-         <node requireChild="true">
-            <name>Repetition</name>
-            <description>When the same type of event such as a fixation on the exact same object happens multiple times and it might be necessary to distinguish the first look vs. others</description>
-            <node takesValue="true">
-               <name>#</name>
+            <node>
+               <name>Hotter-than</name>
             </node>
          </node>
       </node>
@@ -1951,8 +2358,13 @@
          <node relatedTag="True">
             <name>False</name>
          </node>
-         <node relatedTag="Unfamiliar">
+         <node relatedTag="Famous">
             <name>Familiar</name>
+            <description>Something known or encountered before.</description>
+         </node>
+         <node relatedTag="Unfamiliar">
+            <name>Famous</name>
+            <description>Something known by many people but necessarily because of direct contact.</description>
          </node>
          <node relatedTag="Medium">
             <name>High</name>
@@ -2018,8 +2430,9 @@
          <node relatedTag="Constrained">
             <name>Unconstrained</name>
          </node>
-         <node relatedTag="Familiar">
+         <node relatedTag="Famous">
             <name>Unfamiliar</name>
+            <description>Something not known or encountered before.</description>
          </node>
          <node>
             <name>Unmasked</name>
@@ -2038,9 +2451,6 @@
          <node>
             <name>Abstract</name>
          </node>
-         <node>
-            <name>Biological</name>
-         </node>
          <node>
             <name>Clinical</name>
          </node>
@@ -2050,15 +2460,6 @@
          <node>
             <name>Composite</name>
          </node>
-         <node>
-            <name>Electrical</name>
-         </node>
-         <node>
-            <name>Mechanical</name>
-         </node>
-         <node>
-            <name>Magnetic</name>
-         </node>
          <node>
             <name>Numerical</name>
          </node>
@@ -2073,13 +2474,13 @@
             <node>
                <name>Audible</name>
             </node>
-            <node requireChild="true">
+            <node>
                <name>Frequency</name>
                <node isNumeric="true" takesValue="true" unitClass="frequency">
                   <name>#</name>
                </node>
             </node>
-            <node requireChild="true">
+            <node>
                <name>Loudness</name>
                <node isNumeric="true" takesValue="true">
                   <name>#</name>
@@ -2160,10 +2561,10 @@
          </node>
          <node>
             <name>Visual</name>
-            <node requireChild="true">
+            <node>
                <name>Luminance</name>
             </node>
-            <node requireChild="true">
+            <node>
                <name>Color</name>
                <node>
                   <name>Color-shade</name>
@@ -2652,338 +3053,87 @@
                      <node>
                         <name>Azure</name>
                      </node>
-                     <node>
-                        <name>AliceBlue</name>
-                     </node>
-                     <node>
-                        <name>GhostWhite</name>
-                     </node>
-                     <node>
-                        <name>WhiteSmoke</name>
-                     </node>
-                     <node>
-                        <name>SeaShell</name>
-                     </node>
-                     <node>
-                        <name>Beige</name>
-                     </node>
-                     <node>
-                        <name>OldLace</name>
-                     </node>
-                     <node>
-                        <name>FloralWhite</name>
-                     </node>
-                     <node>
-                        <name>Ivory</name>
-                     </node>
-                     <node>
-                        <name>AntiqueWhite</name>
-                     </node>
-                     <node>
-                        <name>Linen</name>
-                     </node>
-                     <node>
-                        <name>LavenderBlush</name>
-                     </node>
-                     <node>
-                        <name>MistyRose</name>
-                     </node>
-                  </node>
-               </node>
-            </node>
-            <node>
-               <name>View</name>
-               <node>
-                  <name>2D-view</name>
-               </node>
-               <node>
-                  <name>3D-view</name>
-               </node>
-               <node>
-                  <name>Background-view</name>
-               </node>
-               <node>
-                  <name>Bistable-view</name>
-                  <description>Something having two stable visual forms that have two distinguishable stable forms as in optical illusions.</description>
-               </node>
-               <node>
-                  <name>Foreground-view</name>
-               </node>
-               <node>
-                  <name>Foveal-view</name>
-                  <description>Visual presentation directly on the fovea.</description>
-               </node>
-               <node>
-                  <name>Map-view</name>
-                  <description>A representation of a geographical location.</description>
-                  <node>
-                     <name>Aerial-view</name>
-                     <description>A representation from an overhead view such as captured by a drone.</description>
-                  </node>
-                  <node>
-                     <name>Satellite-view</name>
-                     <description>A representation as captured by technology such as a satellite.</description>
-                  </node>
-                  <node>
-                     <name>Street-view</name>
-                     <description>A panoramic view from a position on the ground.</description>
-                  </node>
-               </node>
-               <node>
-                  <name>Peripheral-view</name>
-               </node>
-            </node>
-         </node>
-      </node>
-      <node extensionAllowed="true">
-         <name>Spatiotemporal</name>
-         <node>
-            <name>Rate-of-change</name>
-            <node>
-               <name>Acceleration</name>
-               <node isNumeric="true" takesValue="true" unitClass="acceleration">
-                  <name>#</name>
-               </node>
-            </node>
-            <node>
-               <name>Jerk-rate</name>
-               <description>Rate at which an object's acceleration changes with respect to time.</description>
-               <node isNumeric="true" takesValue="true" unitClass="jerk">
-                  <name>#</name>
-               </node>
-            </node>
-            <node requireChild="true">
-               <name>Sampling-rate</name>
-               <description>Number of measurements per unit time.</description>
-               <node takesValue="true" unitClass="frequency">
-                  <name>#</name>
-               </node>
-            </node>
-            <node requireChild="true">
-               <name>Refresh-rate</name>
-               <description>Number of times per second a device displays an image. Also known as the frame rate.</description>
-               <node isNumeric="true" takesValue="true">
-                  <name>#</name>
-               </node>
-            </node>
-            <node>
-               <name>Temporal-rate</name>
-               <description>Number of occurrences in a unit of time.</description>
-               <node isNumeric="true" takesValue="true">
-                  <name>#</name>
-               </node>
-            </node>
-            <node>
-               <name>Velocity</name>
-               <node isNumeric="true" takesValue="true" unitClass="speed">
-                  <name>#</name>
-               </node>
-            </node>
-         </node>
-         <node>
-            <name>Resolution</name>
-            <node requireChild="true">
-               <name>IO-resolution</name>
-               <node requireChild="true">
-                  <name>Screen-resolution</name>
-                  <description>Number of pixels in each dimension for a digital display device.</description>
-                  <node takesValue="true">
-                     <name>#</name>
-                  </node>
-               </node>
-               <node requireChild="true">
-                  <name>Printer-resolution</name>
-                  <description>Number of dots-per-inch for a printer.</description>
-                  <node takesValue="true">
-                     <name>#</name>
-                  </node>
-               </node>
-            </node>
-            <node requireChild="true">
-               <name>Spatial-resolution</name>
-               <description>Linear spacing of a spatial measurement.</description>
-               <node takesValue="true">
-                  <name>#</name>
-               </node>
-            </node>
-            <node requireChild="true">
-               <name>Spectral-resolution</name>
-               <node takesValue="true">
-                  <name>#</name>
-               </node>
-            </node>
-            <node requireChild="true">
-               <name>Temporal-resolution</name>
-               <node takesValue="true">
-                  <name>#</name>
-               </node>
-            </node>
-         </node>
-         <node>
-            <name>Spatial</name>
-            <node>
-               <name>Direction</name>
-               <node isNumeric="true" takesValue="true">
-                  <name>#</name>
-               </node>
-            </node>
-            <node requireChild="true">
-               <name>Distance</name>
-               <description>Distance from a specified position in a specified Direction or grouped with an item from which the distance is measured.</description>
-               <node isNumeric="true" takesValue="true" unitClass="physicalLength">
-                  <name>#</name>
-               </node>
-            </node>
-            <node>
-               <name>Orientation</name>
-               <node isNumeric="true" takesValue="true">
-                  <name>#</name>
-               </node>
-            </node>
-            <node requireChild="true">
-               <name>Position</name>
-               <description>Coordinates with respect a specified frame of reference or the default Screen-frame if no frame is given.</description>
-               <node requireChild="true">
-                  <name>X-position</name>
-                  <node isNumeric="true" takesValue="true" unitClass="physicalLength">
-                     <name>#</name>
-                  </node>
-               </node>
-               <node requireChild="true">
-                  <name>Y-position</name>
-                  <node isNumeric="true" takesValue="true" unitClass="physicalLength">
-                     <name>#</name>
-                  </node>
-               </node>
-               <node requireChild="true">
-                  <name>Z-position</name>
-                  <node isNumeric="true" takesValue="true" unitClass="physicalLength">
-                     <name>#</name>
+                     <node>
+                        <name>AliceBlue</name>
+                     </node>
+                     <node>
+                        <name>GhostWhite</name>
+                     </node>
+                     <node>
+                        <name>WhiteSmoke</name>
+                     </node>
+                     <node>
+                        <name>SeaShell</name>
+                     </node>
+                     <node>
+                        <name>Beige</name>
+                     </node>
+                     <node>
+                        <name>OldLace</name>
+                     </node>
+                     <node>
+                        <name>FloralWhite</name>
+                     </node>
+                     <node>
+                        <name>Ivory</name>
+                     </node>
+                     <node>
+                        <name>AntiqueWhite</name>
+                     </node>
+                     <node>
+                        <name>Linen</name>
+                     </node>
+                     <node>
+                        <name>LavenderBlush</name>
+                     </node>
+                     <node>
+                        <name>MistyRose</name>
+                     </node>
                   </node>
                </node>
             </node>
             <node>
-               <name>Reference-frame</name>
-               <description>A frame of reference used to express spatial attributes.</description>
+               <name>View</name>
                <node>
-                  <name>Custom-frame</name>
-                  <description>A custom frame of reference which should be defined in terms of one of the default frames of reference or by a library.</description>
+                  <name>2D-view</name>
                </node>
                <node>
-                  <name>Room-frame</name>
+                  <name>3D-view</name>
                </node>
                <node>
-                  <name>Screen-frame</name>
-                  <description>HED default frame of reference with origin at center of screen used to display experimental stimuli. Looking out of the screen has +X is horizontal to the right. +Y is up and +Z is out of the front of the screen.</description>
+                  <name>Background-view</name>
                </node>
                <node>
-                  <name>Screen-facing-frame</name>
-                  <description>HED default frame of reference with origin at the subject Nasion. +X is horizontal to the subject right. +Y is to the subject front and +Z is vertical upward.</description>
+                  <name>Bistable-view</name>
+                  <description>Something having two stable visual forms that have two distinguishable stable forms as in optical illusions.</description>
                </node>
                <node>
-                  <name>World-frame</name>
-               </node>
-            </node>
-            <node requireChild="true">
-               <name>Size</name>
-               <node requireChild="true">
-                  <name>Area</name>
-                  <node isNumeric="true" takesValue="true" unitClass="area">
-                     <name>#</name>
-                  </node>
-               </node>
-               <node requireChild="true">
-                  <name>Length</name>
-                  <description>A measurement of the size along the longest dimension.</description>
-                  <node isNumeric="true" takesValue="true" unitClass="physicalLength">
-                     <name>#</name>
-                  </node>
+                  <name>Foreground-view</name>
                </node>
                <node>
-                  <name>Width</name>
-                  <description>A measurement along the shortest of two or three dimensions.</description>
-                  <node isNumeric="true" takesValue="true" unitClass="physicalLength">
-                     <name>#</name>
-                  </node>
+                  <name>Foveal-view</name>
+                  <description>Visual presentation directly on the fovea.</description>
                </node>
                <node>
-                  <name>Height</name>
-                  <description>A measurement in a direction perpendicular to Length and Width in 3D. In 2D, it is perpendicular to both the Length and Height.</description>
-                  <node isNumeric="true" takesValue="true" unitClass="physicalLength">
-                     <name>#</name>
+                  <name>Map-view</name>
+                  <description>A representation of a geographical location.</description>
+                  <node>
+                     <name>Aerial-view</name>
+                     <description>A representation from an overhead view such as captured by a drone.</description>
                   </node>
-               </node>
-               <node requireChild="true">
-                  <name>Volume</name>
-                  <node isNumeric="true" takesValue="true" unitClass="volume">
-                     <name>#</name>
+                  <node>
+                     <name>Satellite-view</name>
+                     <description>A representation as captured by technology such as a satellite.</description>
+                  </node>
+                  <node>
+                     <name>Street-view</name>
+                     <description>A panoramic view from a position on the ground.</description>
                   </node>
-               </node>
-            </node>
-            <node>
-               <name>Surface-properties</name>
-               <node>
-                  <name>Rough</name>
                </node>
                <node>
-                  <name>Smooth</name>
-               </node>
-            </node>
-         </node>
-         <node>
-            <name>Temporal</name>
-            <node requireChild="true">
-               <name>Delay</name>
-               <description>An indicator that the associated tag group represents something temporally offset from the current event. The delay value can be positive or negative and should always appear in a tag group indicating what is delayed.</description>
-               <node isNumeric="true" takesValue="true" unitClass="time">
-                  <name>#</name>
-               </node>
-            </node>
-            <node requireChild="true">
-               <name>Duration</name>
-               <description>An offset that is implicit after duration time passed from the onset.</description>
-               <node isNumeric="true" takesValue="true" unitClass="time">
-                  <name>#</name>
-               </node>
-            </node>
-            <node>
-               <name>Onset</name>
-            </node>
-            <node>
-               <name>Offset</name>
-            </node>
-            <node>
-               <name>Pause</name>
-               <description>Indicating something stopped such as a break or waiting for input.</description>
-            </node>
-            <node requireChild="true">
-               <name>Temporal-value</name>
-               <node isNumeric="true" takesValue="true" unitClass="time">
-                  <name>#</name>
+                  <name>Peripheral-view</name>
                </node>
             </node>
-            <node>
-               <name>Time-out</name>
-            </node>
-            <node>
-               <name>Time-sync</name>
-               <description>Indicates a marker inserted into the recorded data to allow post hoc synchronization of concurrently recorded data streams.</description>
-            </node>
-         </node>
-      </node>
-      <node>
-         <name>Thermodynamic</name>
-         <node>
-            <name>Energy</name>
-         </node>
-         <node>
-            <name>Pressure</name>
-         </node>
-         <node>
-            <name>Temperature</name>
-            <node isNumeric="true" takesValue="true">
-               <name>#</name>
-            </node>
          </node>
       </node>
       <node extensionAllowed="true">
@@ -3051,111 +3201,6 @@
       </node>
       <node>
          <name>Anatomical</name>
-         <node>
-            <name>Body-part</name>
-            <node>
-               <name>Head</name>
-               <node>
-                  <name>Hair</name>
-               </node>
-               <node>
-                  <name>Ear</name>
-               </node>
-               <node>
-                  <name>Face</name>
-                  <node>
-                     <name>Cheek</name>
-                  </node>
-                  <node>
-                     <name>Chin</name>
-                  </node>
-                  <node>
-                     <name>Eye</name>
-                  </node>
-                  <node>
-                     <name>Eyebrow</name>
-                  </node>
-                  <node>
-                     <name>Forehead</name>
-                  </node>
-                  <node>
-                     <name>Lip</name>
-                  </node>
-                  <node>
-                     <name>Nose</name>
-                  </node>
-                  <node>
-                     <name>Mouth</name>
-                  </node>
-                  <node>
-                     <name>Teeth</name>
-                  </node>
-               </node>
-            </node>
-            <node>
-               <name>Arm</name>
-               <node>
-                  <name>Elbow</name>
-               </node>
-               <node>
-                  <name>Hand</name>
-                  <node>
-                     <name>Finger</name>
-                     <node>
-                        <name>Index-finger</name>
-                     </node>
-                     <node>
-                        <name>Little-finger</name>
-                     </node>
-                     <node>
-                        <name>Middle-finger</name>
-                     </node>
-                     <node>
-                        <name>Ring-finger</name>
-                     </node>
-                     <node>
-                        <name>Thumb</name>
-                     </node>
-                  </node>
-               </node>
-               <node>
-                  <name>Wrist</name>
-               </node>
-            </node>
-            <node>
-               <name>Leg</name>
-               <node>
-                  <name>Ankle</name>
-               </node>
-               <node>
-                  <name>Foot</name>
-                  <node>
-                     <name>Toe</name>
-                  </node>
-               </node>
-               <node>
-                  <name>Knee</name>
-               </node>
-            </node>
-            <node>
-               <name>Torso</name>
-               <node>
-                  <name>Upper-torso</name>
-                  <node>
-                     <name>Chest</name>
-                  </node>
-               </node>
-               <node>
-                  <name>Lower-torso</name>
-                  <node>
-                     <name>Hips</name>
-                  </node>
-               </node>
-               <node>
-                  <name>Waist</name>
-               </node>
-            </node>
-         </node>
       </node>
    </node>
    <unitClasses>
diff --git a/hedweb/tests/data/ValidTwoColumnHED7_1_1.tsv b/hedweb/tests/data/ValidTwoColumnHED7_1_1.tsv
new file mode 100644
index 000000000..db7629c10
--- /dev/null
+++ b/hedweb/tests/data/ValidTwoColumnHED7_1_1.tsv
@@ -0,0 +1,5 @@
+Column1: Event Code	Column2: Combined tag
+1250	Event/Label/StartAreaE, Event/Category/Experiment control/Sequence/Block/Guard Area E, Event/Description/Start guarding Area E
+1311	Event/Label/ValidImage, Event/Category/Experimental stimulus, Event/Description/Show a valid ID to be verified, Item/Object/IDCard, Sensory presentation/Visual, Participant/Effect/Visual, Participant/Effect/Cognitive/Valid
+2120	Event/Label/ButtonDeny, Event/Description/Button push to deny access to the ID holder, Event/Category/Participant response, (Participant ~ Action/Button press/Keyboard ~ Participant/Effect/Body part/Arm/Hand/Finger),  (Participant ~ Action/Deny ~ Item/Object/Person/IDHolder)
+3111	Event/Label/TakeSurvey, Event/Category/Experimental stimulus/Instruction, Event/Description/Tell subject to start survey
diff --git a/hedweb/tests/data/example_dictionary.json b/hedweb/tests/data/example_dictionary.json
new file mode 100644
index 000000000..0f817becd
--- /dev/null
+++ b/hedweb/tests/data/example_dictionary.json
@@ -0,0 +1,82 @@
+{
+    "onset": {
+        "Description": "Onset of the go signal which is the disappearance of the cue bar",
+        "Units": "s"
+    },
+    "duration": {
+        "Description": "Duration of the event",
+        "Units": "s"
+    },
+	"event-type": {
+		"Description": "Subject presses both buttons causing cue bar to start count-down display",
+		"Levels" : {
+            "cue": "Subject presses both buttons causing cue bar to start count-down display",
+            "go": "Bar disappears cueing the subject to make a choice about which hand to raise",
+            "right-raised": "Subject releases right button and raises right hand",
+            "left-raised": "Subject releases left button and raises left hand",
+            "left-raised-match": "Subject raised left hand and computer presented image of left hand.",
+            "left-raised-nomatch": "Subject raised left hand and computer presented image of right hand.",
+            "right-raised-match": "Subject raised right hand and computer presented image of right hand.",
+            "right-raised-nomatch": "Subject raised right hand and computer presented image of left hand."
+        },
+        "HED": {
+          "cue": "Description/Subject presses both buttons causing cue bar to start count-down display, (Sensory-event, Visual, Cue, ((Rectangle, (Position, (Center-of, Screen)), (Size, Decreasing)), Appear)), (Agent-action, Experiment-subject, (Press, Hold-down, (Left-side, Button)), (Press, Hold-down, (Right-side, Button)))",
+          "go": "Description/Onset of the go signal which is the disappearance of the cue bar, Sensory-event, Visual, Experimental-stimulus, Instructional, Signal-go,(Rectangle, Disappear)",
+          "right-raised": "Description/Subject raised right hand thereby releasing right button, (Agent-action, Experiment-subject, (Lift, (Right-side, Hand)), (Release, (Right-side, Button)))",
+          "left-raised": "Description/Subject raised left hand thereby releasing left button, (Agent-action, Experiment-subject, (Lift, (Left-side, Hand)), (Release, (Left-side, Button)))",
+          "left-raised-match": "Description/Computer presented image of left hand thereby matching a previously raised left hand, (Sensory-event, Visual, (Drawing, ID/left_hand.png,(Left-side, Hand)), (Feedback, (Intended-effect, Penalty)))",
+          "left-raised-nomatch": "Description/Computer presented image of left hand thereby not matching previously raised right hand, (Sensory-event, Visual, (Drawing, ID/right_hand.png, (Left-side, Hand)), (Feedback, (Intended-effect, Reward)))",
+          "right-raised-match": "Description/Computer presented image of right hand thereby matching a previously raised right hand, (Sensory-event, Visual, (Drawing, ID/right_hand.png,(Right-side, Hand)), (Feedback, (Intended-effect, Penalty)))",
+          "right-raised-nomatch": "Description/Computer presented image of right hand thereby not matching a previously raised left hand, (Sensory-event, Visual, (Drawing, ID/right_hand.png, (Right-side, Hand)), (Feedback, (Intended-effect, Reward)))"
+        }
+	},
+    "sample": {
+        "Description": "The sample within the EEG data at which an event occurred"
+    },
+    "stage": {
+        "Description": "The current stage of the experiment.",
+        "Levels": {
+            "1": "Stage 1. BCI acts randomly",
+            "2": "Stage 2. BCI was trained on data from stage 1.",
+            "3": "Stage 3. BCI was trained on data from stages 1 and 2."
+        },
+		"HED": {
+            "1": "Description/BCI acts randomly, (Definition/Random-selection, Condition-variable, (Random, Predict)), Label-def/Random-selection",
+            "2": "Description/BCI was trained on data from stage 1., (Definition/Trained-on-random, Condition-variable), Label-def/Trained-on-random",
+            "3": "Description/BCI was trained on data from stages 1 and 2., (Definition/Trained-on-all, Condition-variable), Label-def/Trained-on-all"
+		}
+    },
+	"trial": {
+		"Description": "The number of the trial in the experiment.",
+		"HED": "Description/The number of the trial in the experiment, (Trial, Item-count/#)"
+	},
+    "latency": {
+        "Description": "Estimated beginning of the data chunk used for the BCI prediction with respect to the true event as measured by parallel port trigger in this trial.",
+        "Units": "ms"
+    },
+    "bci_prediction": {
+        "Description": "Which hand did the BCI predict was raised in this trial.",
+        "Levels": {
+            "left": "BCI predicts participant raised left hand",
+            "right": "BCI predicts participant raised right hand"
+        },
+		"HED": {
+          "left": "Description/BCI predicts participant raised left hand, Computed-prediction, (Computational-evidence, (Left-side, Hand))",
+          "right": "Description/BCI predicts participant raised right hand, Computed-prediction, (Computational-evidence, (Right-side, Hand))"
+        }
+    },
+    "bci_prediction_valid": {
+        "Description": "Did a prediction arrive on time (if not, a random prediction was made) in this trial.",
+        "Levels": {
+            "1": "Computational prediction arrived on time",
+            "2": "Computational prediction was late so random prediction was used."
+        },
+		"HED": {
+			"1": "Description/Computational prediction arrived on time, (Computational-agent, Predict)",
+			"2": "Description/Computational prediction was late so random prediction was used., Time-out, (Random, Predict)"
+		}
+    },
+    "n_repeated": {
+        "Description": "Number of trials that had to be repeated until the present trial because of invalid participant behavior (within this stage)."
+    }
+}
\ No newline at end of file
diff --git a/hedweb/tests/data/short_form_valid.json b/hedweb/tests/data/short_form_valid.json
new file mode 100644
index 000000000..a791a0f69
--- /dev/null
+++ b/hedweb/tests/data/short_form_valid.json
@@ -0,0 +1,21 @@
+{
+    "onset": {
+        "Description": "Onset of the go signal which is the disappearance of the cue bar",
+        "Units": "s"
+    },
+    "duration": {
+        "Description": "Duration of the event",
+        "Units": "s"
+    },
+	"event-type": {
+      "Description": "Subject presses both buttons causing cue bar to start count-down display",
+      "Levels": {
+        "cue": "Subject presses both buttons causing cue bar to start count-down display",
+        "go": "Bar disappears cueing the subject to make a choice about which hand to raise"
+      },
+      "HED": {
+        "cue": "Description/Subject presses both buttons causing cue bar to start count-down display, (Sensory-event, Visual, Cue, ((Rectangle, (Position, (Center-of, Screen)), (Size, Decreasing)))), (Agent-action, Experiment-participant, (Press, Hold-down, (Left-side, Button)), (Press, Hold-down, (Right-side, Button)))",
+        "go": "Description/Onset of the go signal which is the disappearance of the cue bar, Sensory-event, Visual, Experimental-stimulus, Instructional, Signal-go"
+      }
+    }
+}
\ No newline at end of file
diff --git a/hedweb/tests/test_dictionary.py b/hedweb/tests/test_dictionary.py
index 1e9b1409f..30dd3b795 100644
--- a/hedweb/tests/test_dictionary.py
+++ b/hedweb/tests/test_dictionary.py
@@ -1,62 +1,65 @@
-import unittest
-# from flask import current_app, jsonify, Response
-# from hed.web.utils import app_config
-# from hed.web.validation import generate_dictionary_validation_filename
-# from hed.web.app_factory import AppFactory
-# from hed.web.constants import file_constants, spreadsheet_constants
+
 import os
+import shutil
+import unittest
 
-# app = AppFactory.create_app('config.TestConfig')
-# with app.app_context():
-#     from hed.web import web_utils
-#     from hed.web.routes import route_blueprint
-#
-#     app.register_blueprint(route_blueprint, url_prefix=app.config['URL_PREFIX'])
-#     web_utils.create_upload_directory(app.config['UPLOAD_FOLDER'])
+from hed.web.app_factory import AppFactory
 
 
-class Test(unittest.TestCase):
-    def setUp(self):
-        print("Stuff")
-        # self.create_test_app()
-        # self.app = app.app.test_client()
-        # self.major_version_key = 'major_versions'
-        # self.hed_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED.xml')
-        # self.tsv_file1 = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/tsv_file1.txt')
-
-    # def create_test_app(self):
-    #     app = AppFactory.create_app('config.TestConfig')
-    #     with app.app_context():
-    #         from hed.web.routes import route_blueprint
-    #         app.register_blueprint(route_blueprint)
-    #         self.app = app.test_client()
+def test_dictionaries():
+    base_path = os.path.dirname(os.path.abspath(__file__))
+    test_dict = {'hed-xml-file': os.path.join(base_path, 'data/HED8.0.0-alpha.1.xml'),
+                 'hed-display-name': 'HED8.0.0-alpha.1.xml',
+                 'json-path': os.path.join(base_path, 'data/short_form_valid.json'),
+                 'json-file': 'short_form_valid.json',
+                 'check-for-warnings': True}
+    return test_dict
 
-    def test_generate_input_arguments_from_validation_form(self):
-        self.assertTrue(1, "Testing generate_arguments_from_dictionary_form")
 
-    def test_generate_spreadsheet_validation_filename(self):
-        self.assertTrue(1, "Testing generate_dictionary_validation_filename")
-        # dictionary_filename = 'abc.xls'
-        # expected_spreadsheet_filename = 'validated_' + dictionary_filename.rsplit('.')[0] + '.txt'
-        # validation_file_name = generate_dictionary_validation_filename(dictionary_filename, worksheet_name='')
-        # self.assertTrue(validation_file_name)
-        # self.assertEqual(expected_spreadsheet_filename, validation_file_name)
+class Test(unittest.TestCase):
+    @classmethod
+    def setUpClass(cls):
+        cls.upload_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/upload')
+        app = AppFactory.create_app('config.TestConfig')
+        with app.app_context():
+            from hed.web.routes import route_blueprint
+            app.register_blueprint(route_blueprint)
+            if not os.path.exists(cls.upload_directory):
+                os.mkdir(cls.upload_directory)
+            app.config['UPLOAD_FOLDER'] = cls.upload_directory
+            cls.app = app
+            cls.app.test = app.test_client()
 
-    def test_get_uploaded_file_paths_from_forms(self):
-        self.assertTrue(1, "Testing get_uploaded_file_paths_from_forms")
+    @classmethod
+    def tearDownClass(cls):
+        shutil.rmtree(cls.upload_directory)
 
-    def test_report_eeg_events_validation_status(self):
-        self.assertTrue(1, "Testing report_eeg_events_validation_status")
+    def test_generate_input_arguments_from_validation_form(self):
+        from hed.web import dictionary
+        self.assertRaises(TypeError, dictionary.generate_arguments_from_dictionary_form, {},
+                          "An exception should be raised if an empty request is passed")
 
-    def test_report_spreadsheet_validation_status(self):
+    def test_report_dictionary_validation_status(self):
         self.assertTrue(1, "Testing report_dictionary_validation_status")
 
-    def test_save_validation_issues_to_file_in_upload_folder(self):
-        self.assertTrue(1, "Testing save_issues_to_upload_folder")
-
-    def test_validate_spreadsheet(self):
-        self.assertTrue(1, "Testing validate_dictionary")
+    def test_validate_dictionary(self):
+        from hed.web.dictionary import validate_dictionary
+        from hed.schema import hed_schema_file
+        inputs = test_dictionaries()
+        self.assertEqual("", validate_dictionary(inputs), "This dictionary should have no errors for 8.0.0")
+        hed8_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
 
+        hed7_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
+        hed7 = hed_schema_file.load_schema(hed7_path)
+        # response = validate_dictionary(inputs, hed_schema=hed7)
+        # inputs["hed-xml-file"] = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
+        # hed7 = hed_schema_file.load_schema(inputs["hed-xml_file"])
+        # self.assertEqual("", validate_dictionary(input_arguments, hed_schema=schema8),
+        #                  "This dictionary should have no errors for directly created 8.0.0")
+        # schema7 = HedSchema(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml'))
+        # x = validate_dictionary(input_arguments, hed_schema=schema7, return_response=False)
+        # self.assertEqual("", validate_dictionary(input_arguments, hed_schema=schema7,return_response=False),
+        #                  "This dictionary should have no errors for directly created 8.0.0")
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/hedweb/tests/test_routes.py b/hedweb/tests/test_routes.py
index f2ac8efd7..15ae56b31 100644
--- a/hedweb/tests/test_routes.py
+++ b/hedweb/tests/test_routes.py
@@ -27,8 +27,8 @@ def tearDownClass(cls):
         shutil.rmtree(cls.upload_directory)
 
     def test_delete_file_in_upload_directory(self):
-        response = self.app.test.post('/delete/file_that_does_not_exist')
-        # self.assertEqual(response.status_code, 404, "Non-existent file should cause a non-found error")
+        response = self.app.test.get('/delete/file_that_does_not_exist')
+        self.assertEqual(response.status_code, 404, "Non-existent file should cause a non-found error")
         hed_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED.xml')
         dummy_file = os.path.join(self.app.config['UPLOAD_FOLDER'], 'HED.xml')
         dummy_path = pathlib.Path(dummy_file)
@@ -50,28 +50,28 @@ def test_download_file_in_upload_directory(self):
         copyfile(hed_file, dummy_file)
         self.assertTrue(dummy_path.is_file(), "Dummy file now exists")
         response = self.app.test.get('/download-file/HED.xml')
-        print(response.status_code)
+
         # self.assertEqual(response.status_code, 204, "Dummy file should be deleted")
 
     def test_get_dictionary_validation_results(self):
-        response = self.app.get('/dictionary-validation-submit')
-        self.assertEqual(response.status_code, 404)
+        response = self.app.test.post('/dictionary-validation-submit')
+        self.assertEqual(400, response.status_code, 'Dictionary validation requires data')
 
     def test_get_events_validation_results(self):
-        response = self.app.get('/events-validation-submit')
-        self.assertEqual(response.status_code, 404)
+        response = self.app.test.post('/events-validation-submit')
+        self.assertEqual(400, response.status_code, 'Event validation requires data')
 
     def test_get_hed_services_results(self):
-        response = self.app.get('/hed-services-submit')
-        self.assertEqual(response.status_code, 404)
+        response = self.app.test.get('/hed-services-submit')
+        self.assertEqual(405, response.status_code, 'HED services require data')
 
     def test_get_hed_version(self):
-        response = self.app.post('/get-hed-version')
-        self.assertEqual(response.status_code, 400)
+        response = self.app.test.post('/get-hed-version')
+        self.assertEqual(400, response.status_code, 'Returning HED version requires data')
 
     def test_get_major_hed_versions(self):
-        response = self.app.post('/get-hed-major-versions')
-        self.assertEqual(response.status_code, 405)
+        response = self.app.test.post('/get-hed-major-versions')
+        self.assertEqual(405, response.status_code, 'Returning HED version list requires data')
         # import hed.web.constants.common_constants as constants
         # from hed.web.web_utils import find_major_hed_versions
         # hed_info = find_major_hed_versions()
@@ -79,60 +79,60 @@ def test_get_major_hed_versions(self):
         # self.assertTrue('7.1.2' in hed_info[constants.HED_MAJOR_VERSIONS], "7.1.2 is a major versions")
 
     def test_get_schema_compliance_check_results(self):
-        response = self.app.post('/schema-compliance-check-submit')
-        self.assertEqual(response.status_code, 400)
+        response = self.app.test.post('/schema-compliance-check-submit')
+        self.assertEqual(400, response.status_code, 'Checking schema compliance requires data')
 
     def test_get_schema_conversion_results(self):
-        response = self.app.post('/schema-conversion-submit')
-        self.assertEqual(response.status_code, 400)
+        response = self.app.test.post('/schema-conversion-submit')
+        self.assertEqual(400, response.status_code, 'Converting schema requires data')
 
     def test_get_spreadsheet_columns_info(self):
-        response = self.app.post('/get-spreadsheet-columns-info')
-        self.assertEqual(response.status_code, 400)
+        response = self.app.test.post('/get-spreadsheet-columns-info')
+        self.assertEqual(400, response.status_code, 'Returning spreadsheet column info requires data')
 
     def test_get_spreadsheet_validation_results(self):
-        response = self.app.post('/spreadsheet-validation-submit')
-        self.assertEqual(response.status_code, 400)
+        response = self.app.test.post('/spreadsheet-validation-submit')
+        self.assertEqual(400, response.status_code, 'Validating spreadsheet requires data')
 
     def test_get_worksheets_info(self):
-        response = self.app.post('/get-worksheets-info')
-        self.assertEqual(response.status_code, 400)
+        response = self.app.test.post('/get-worksheets-info')
+        self.assertEqual(400, response.status_code, 'Returning worksheet info requires data')
 
     def test_render_additional_examples_page(self):
         response = self.app.test.get('/additional-examples')
-        self.assertEqual(response.status_code, 200, "The additional-examples content page exists")
+        self.assertEqual(response.status_code, 200, "The additional-examples content page should exist")
 
     def test_render_common_error_page(self):
         response = self.app.test.get('/common-errors')
-        self.assertEqual(response.status_code, 200, "The common-errors content page exists")
+        self.assertEqual(response.status_code, 200, "The common-errors content page should exist")
 
     def test_render_dictionary_form(self):
-        response = self.app.get('/dictionary')
-        self.assertEqual(response.status_code, 200)
+        response = self.app.test.get('/dictionary')
+        self.assertEqual(response.status_code, 200, "The dictionary content page should exist")
 
     def test_render_events_form(self):
-        response = self.app.get('/events')
-        self.assertEqual(response.status_code, 200)
+        response = self.app.test.get('/events')
+        self.assertEqual(response.status_code, 200, "The events content page should exist")
 
     def test_render_hed_services_form(self):
-        response = self.app.get('/hed-services')
-        self.assertEqual(response.status_code, 200)
+        response = self.app.test.get('/hed-services')
+        self.assertEqual(response.status_code, 200, "The hed-services content page should exist")
 
     def test_render_help_page(self):
-        response = self.app.get('/hed-tools-help')
-        self.assertEqual(response.status_code, 200)
+        response = self.app.test.get('/hed-tools-help')
+        self.assertEqual(response.status_code, 200, "The hed-tools-help content page should exist" )
 
     def test_render_home_page(self):
-        response = self.app.get('/')
-        self.assertEqual(response.status_code, 200)
+        response = self.app.test.get('/')
+        self.assertEqual(response.status_code, 200, "The root hed home page should exist")
 
     def test_render_schema_form(self):
-        response = self.app.get('/schema')
-        self.assertEqual(response.status_code, 200)
+        response = self.app.test.get('/schema')
+        self.assertEqual(response.status_code, 200, "The schema page should exist")
 
     def test_render_spreadsheet_form(self):
-        response = self.app.get('/spreadsheet')
-        self.assertEqual(response.status_code, 200)
+        response = self.app.test.get('/spreadsheet')
+        self.assertEqual(response.status_code, 200, "The spreadsheet page should exist")
 
 
 if __name__ == '__main__':
diff --git a/hedweb/tests/test_schema.py b/hedweb/tests/test_schema.py
index 6950cefc8..0134f9567 100644
--- a/hedweb/tests/test_schema.py
+++ b/hedweb/tests/test_schema.py
@@ -34,7 +34,7 @@ def test_generate_input_from_schema_form(self):
         self.assertTrue(1, "Testing generate_input_from_schema_form")
 
     def test_get_schema_conversion(self):
-        self.assertTrue(1, "Testing get_schema_conversion")
+        self.assertTrue(1, "Testing convert_schema")
 
     def test_run_schema_compliance_check(self):
         self.assertTrue(1, "Testing run_schema_compliance_check")
diff --git a/hedweb/tests/test_spreadsheet.py b/hedweb/tests/test_spreadsheet.py
index 282068243..fb23a16a2 100644
--- a/hedweb/tests/test_spreadsheet.py
+++ b/hedweb/tests/test_spreadsheet.py
@@ -1,35 +1,28 @@
-import unittest
-# from flask import current_app, jsonify, Response
-# from hed.web.utils import app_config
-# from hed.web.validation import generate_dictionary_validation_filename
-# from hed.web.app_factory import AppFactory
-# from hed.web.constants import file_constants, spreadsheet_constants
 import os
-
-# app = AppFactory.create_app('config.TestConfig')
-# with app.app_context():
-#     from hed.web import web_utils
-#     from hed.web.routes import route_blueprint
-#
-#     app.register_blueprint(route_blueprint, url_prefix=app.config['URL_PREFIX'])
-#     web_utils.create_upload_directory(app.config['UPLOAD_FOLDER'])
+import shutil
+import unittest
+from unittest import mock
+from flask import Flask, current_app
+from hed.web.app_factory import AppFactory
 
 
 class Test(unittest.TestCase):
-    def setUp(self):
-        print("Stuff")
-        # self.create_test_app()
-        # self.app = app.app.test_client()
-        # self.major_version_key = 'major_versions'
-        # self.hed_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED.xml')
-        # self.tsv_file1 = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/tsv_file1.txt')
-
-    # def create_test_app(self):
-    #     app = AppFactory.create_app('config.TestConfig')
-    #     with app.app_context():
-    #         from hed.web.routes import route_blueprint
-    #         app.register_blueprint(route_blueprint)
-    #         self.app = app.test_client()
+    @classmethod
+    def setUpClass(cls):
+        cls.upload_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/upload')
+        app = AppFactory.create_app('config.TestConfig')
+        with app.app_context():
+            from hed.web.routes import route_blueprint
+            app.register_blueprint(route_blueprint)
+            if not os.path.exists(cls.upload_directory):
+                os.mkdir(cls.upload_directory)
+            app.config['UPLOAD_FOLDER'] = cls.upload_directory
+            cls.app = app
+            cls.app.test = app.test_client()
+
+    @classmethod
+    def tearDownClass(cls):
+        shutil.rmtree(cls.upload_directory)
 
     def test_generate_input_from_validation_form(self):
         self.assertTrue(1, "Testing generate_input_from_validation_form")
@@ -39,6 +32,31 @@ def test_report_spreadsheet_validation_status(self):
 
     def test_validate_spreadsheet(self):
         self.assertTrue(1, "Testing validate_spreadsheet")
+        # from hed.web.spreadsheet import validate_spreadsheet
+        # temp_name = save_file_to_upload_folder('')
+        # self.assertEqual(temp_name, '', "A file with empty name cnnot be copied copied")
+        # some_file = '3k32j23kj1.txt'
+        # temp_name = save_file_to_upload_folder(some_file)
+        # self.assertEqual(temp_name, '', "A file that does not exist cannot be copied")
+        # hed_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED.xml')
+        # self.assertTrue(os.path.exists(hed_file), "The HED.xml file should exist in the data directory")
+        # spreadsheet_path =
+        # input_arguments = {''}
+
+
+def get_inputs():
+    spreadsheet_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/ExcelMultipleSheets.xlsx')
+    hed_file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED7.1.2.xml')
+    spreadsheet_file = 'ExcelMultipleSheets.xlsx'
+    worksheet_name ='LKT Events'
+    tag_columns = [2, 3]
+    other_columns = [4]
+    # inputs = {'spreadsheet_path': spreadsheet_path, 'hed_file_path': hed_file_path, \
+    #           'spreadsheet-file': spreadsheet_file, 'worksheet-name': worksheet_name, \
+    #           'tag-columns': tag_columns, 'has-column-names': true, \
+    #           'check-for-warnings:', True, 'column-prefix-dictionary': ''}
+    inputs = {}
+    return inputs
 
 
 if __name__ == '__main__':
diff --git a/hedweb/tests/test_utils.py b/hedweb/tests/test_spreadsheet_utils.py
similarity index 90%
rename from hedweb/tests/test_utils.py
rename to hedweb/tests/test_spreadsheet_utils.py
index a7924ef67..b4dce4f0d 100644
--- a/hedweb/tests/test_utils.py
+++ b/hedweb/tests/test_spreadsheet_utils.py
@@ -39,7 +39,7 @@ def test_get_column_delimiter_based_on_file_extension(self):
     #     self.assertEqual('', delimiter, "Excel files should have an empty delimiter")
 
     def test_get_excel_worksheet_names(self):
-        from hed.web.utils import get_excel_worksheet_names
+        from hed.web.spreadsheet_utils import get_excel_worksheet_names
         self.excel_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/ExcelMultipleSheets.xlsx')
         worksheet_names = get_excel_worksheet_names(self.excel_file)
         self.assertEqual(len(worksheet_names), 3, "This excel file has three worksheets")
@@ -74,12 +74,6 @@ def test_get_spreadsheet_specific_tag_column_indices(self):
         #     self.assertIsInstance(indices, dict)
         #
 
-    def test_get_spreadsheet_other_tag_column_indices(self):
-        self.assertTrue(1, "Testing get_spreadsheet_other_tag_column_indices")
-
-    def test_get_spreadsheet_specific_tag_column_indices(self):
-        self.assertTrue(1, "Testing get_spreadsheet_specific_tag_column_indices")
-
     def test_get_text_file_column_names(self):
         self.assertTrue(1, "Testing get_text_file_column_names")
         # def test_get_text_file_column_names(self):
@@ -95,11 +89,9 @@ def test_populate_spreadsheet_columns_info_dictionary(self):
         self.assertTrue(1, "Testing populate_spreadsheet_columns_info_dictionary")
 
     def test_populate_worksheets_info_dictionary(self):
+        info = {}
         self.assertTrue(1, "Testing populate_worksheets_info_dictionary")
 
-    def test_worksheet_name_present_in_form(self):
-        self.assertTrue(1, "Testing worksheet_name_present_in_form")
-
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/hedweb/tests/test_web_utils.py b/hedweb/tests/test_web_utils.py
index 817fe53f0..05c275567 100644
--- a/hedweb/tests/test_web_utils.py
+++ b/hedweb/tests/test_web_utils.py
@@ -28,18 +28,6 @@ def setUpClass(cls):
     def tearDownClass(cls):
         shutil.rmtree(cls.upload_directory)
 
-    def test_check_if_option_in_form(self):
-        self.assertTrue(1, "Testing form_has_option")
-        from hed.web.web_utils import form_has_option
-        mock_form = mock.Mock()
-        mock_dict = {'upload': 'me', 'download:': 'them'}
-        mock_form.values = mock_dict
-        self.assertTrue(form_has_option(mock_form, 'upload', 'me'),
-                        "True if option_name has target_value")
-        self.assertFalse(form_has_option(mock_form, 'upload', 'them'),
-                         "False if option_name is not target_value")
-        self.assertFalse(form_has_option(mock_form, 'temp', 'them'),
-                         "False if invalid option name")
 
     def test_convert_number_str_to_list(self):
         from hed.web.web_utils import convert_number_str_to_list
@@ -98,6 +86,25 @@ def test_find_all_str_indices_in_list(self):
         self.assertIsInstance(indices, list)
         self.assertEqual(expected_indices, indices)
 
+    def test_form_has_file(self):
+        self.assertTrue(1, "Testing form_has_file")
+
+    def test_form_has_option(self):
+        self.assertTrue(1, "Testing form_has_option")
+        from hed.web.web_utils import form_has_option
+        mock_form = mock.Mock()
+        mock_dict = {'upload': 'me', 'download:': 'them'}
+        mock_form.values = mock_dict
+        self.assertTrue(form_has_option(mock_form, 'upload', 'me'),
+                        "True if option_name has target_value")
+        self.assertFalse(form_has_option(mock_form, 'upload', 'them'),
+                         "False if option_name is not target_value")
+        self.assertFalse(form_has_option(mock_form, 'temp', 'them'),
+                         "False if invalid option name")
+
+    def test_form_has_url(self):
+        self.assertTrue(1, "Testing form_has_url")
+
     def test_generate_download_file_response(self):
         from hed.web.web_utils import generate_download_file_response
         hed_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED.xml')
@@ -158,7 +165,24 @@ def test_get_optional_form_field(self):
         self.assertTrue(1, "Testing get_optional_form_field")
 
     def test_get_uploaded_file_path_from_form(self):
-        self.assertTrue(1, "Testingget_uploaded_file_path_from_form")
+        from hed.web.web_utils import get_uploaded_file_path_from_form
+        # with self.app.test as client:
+        #     # send data as POST form to endpoint
+        #     sent = {'return_url': 'my_test_url'}
+        #     result = client.post(
+        #         '/',
+        #         data=sent
+        #     )
+        # mock_form = mock.Mock()
+        # mock_dict = {'upload': 'me', 'download:': 'them'}
+        #     mock_form.values = mock_dict
+        # text = 'save me now'
+        # filename = 'test_save.txt'
+        # actual_path = os.path.join(self.upload_directory, filename)
+        # self.assertEqual(0, os.path.isfile(actual_path), f"{actual_path} should not exist before saving")
+        # with self.app.app_context():
+        #     the_path = save_text_to_upload_folder(text, filename)
+        #     self.assertEqual(1, os.path.isfile(the_path), f"{the_path} should exist after saving")
 
     def test_handle_http_error(self):
         error_code = "CODE"
@@ -166,26 +190,51 @@ def test_handle_http_error(self):
 
         self.assertTrue(1, "Testing handle_http_error")
 
-    def test_find_hed_version_in_uploaded_file(self):
-            self.assertTrue(1, "Testing find_hed_version_in_uploaded_file")
-
     def test_save_file_to_upload_folder(self):
-        from hed.web.web_utils import save_file_to_upload_folder, app_config
-        temp_name = save_file_to_upload_folder('')
-        self.assertEqual(temp_name, '', "A file with empty name cnnot be copied copied")
-        some_file = '3k32j23kj1.txt'
-        temp_name = save_file_to_upload_folder(some_file)
-        self.assertEqual(temp_name, '', "A file that does not exist cannot be copied")
+        self.assertTrue(1, "Testing save_file_to_upload_folder")
+        from hed.web.web_utils import save_file_to_upload_folder
+        from werkzeug.datastructures import FileStorage
+        filename = 'HED.xml'
+        actual_path = os.path.join(self.upload_directory, filename)
+        self.assertEqual(0, os.path.isfile(actual_path), f"{actual_path} should not exist before saving")
         hed_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED.xml')
-        self.assertTrue(os.path.exists(hed_file), "The HED.xml file should exist in the data directory")
-        mock_file = mock.Mock()
-        mock_file.filename = hed_file
+        # with open(hed_file) as f:
+        #     upload_file = FileStorage(f, filename='HED.xml', content_type='text/xml',  content_length=0, stream=stream)
+        #     with self.app.app_context():
+        #         the_path = save_file_to_upload_folder(upload_file)
+        #         self.assertEqual(1, os.path.isfile(the_path), f"{the_path} should exist after saving")
+
+        # temp_name = save_file_to_upload_folder('')
+        # self.assertEqual(temp_name, '', "A file with empty name cnnot be copied copied")
+        # some_file = '3k32j23kj1.txt'
+        # temp_name = save_file_to_upload_folder(some_file)
+        # self.assertEqual(temp_name, '', "A file that does not exist cannot be copied")
+        # hed_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED.xml')
+        #
+        # self.assertTrue(os.path.exists(hed_file), "The HED.xml file should exist in the data directory")
+        # actual_path = os.path.join(self.upload_directory, filename)
+        # self.assertEqual(0, os.path.isfile(actual_path), f"{actual_path} should not exist before saving")
+        # with self.app.app_context():
+        #     the_path = save_text_to_upload_folder(text, filename)
+        #     self.assertEqual(1, os.path.isfile(the_path), f"{the_path} should exist after saving")
+        # mock_file = mock.Mock()
+        # mock_file.filename = hed_file
         # TODO: Context not working this is not tested
         # with Test.app_context():
         #     temp_name = save_file_to_upload_folder(mock_file)
         # self.assertNotEqual(mock_file, '', "It should create an actual file in the upload directory")
         # self.assertTrue(os.path.isfile(temp_name), "File should exist after it is uploaded")
 
+    def test_text_to_upload_folder(self):
+        from hed.web.web_utils import save_text_to_upload_folder
+        text = 'save me now'
+        filename = 'test_save.txt'
+        actual_path = os.path.join(self.upload_directory, filename)
+        self.assertEqual(0, os.path.isfile(actual_path), f"{actual_path} should not exist before saving")
+        with self.app.app_context():
+            the_path = save_text_to_upload_folder(text, filename)
+            self.assertEqual(1, os.path.isfile(the_path), f"{the_path} should exist after saving")
+
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/hedweb/tests/testing_dict_val.py b/hedweb/tests/testing_dict_val.py
deleted file mode 100644
index 18d636bcd..000000000
--- a/hedweb/tests/testing_dict_val.py
+++ /dev/null
@@ -1,15 +0,0 @@
-
-from hed.util.column_def_group import ColumnDefGroup
-from hed.util.hed_schema import HedSchema
-
-if __name__ == '__main__':
-    local_hed_xml = "data/HED8.0.0-alpha.1.xml"
-    hed_dictionary = HedSchema(local_hed_xml)
-    json_filename = "data/task_pennies_events.json"
-
-    json_file = ColumnDefGroup(json_filename)
-    # Print all the errors from the json file
-    errors = json_file.validate_entries(hed_dictionary)
-    for error in errors:
-        x = error
-        print(error["message"])
\ No newline at end of file
diff --git a/hedweb/tests/testing_dictionary_validate.py b/hedweb/tests/testing_dictionary_validate.py
new file mode 100644
index 000000000..1ce6916a3
--- /dev/null
+++ b/hedweb/tests/testing_dictionary_validate.py
@@ -0,0 +1,37 @@
+import os
+import json
+import os.path
+from urllib.parse import urlparse
+from shutil import move
+from hed.validator.hed_validator import HedValidator
+from hed.util.column_def_group import ColumnDefGroup
+from hed.schema.hed_schema_file import load_schema
+from hed.util.error_types import ErrorContext
+
+if __name__ == '__main__':
+
+    dir_path = os.path.dirname(os.path.realpath(__file__))
+    local_hed_file1 = os.path.join(dir_path, 'data/HED7.1.2.xml')
+    local_hed_file2 = os.path.join(dir_path, 'data/HED8.0.0-alpha.1.xml')
+    json_file = os.path.join(dir_path, 'data/example_dictionary.json')
+
+    # Example 1
+
+    hed_schema = load_schema(local_hed_file1)
+    json_dictionary = ColumnDefGroup(json_file)
+    issues = json_dictionary.validate_entries(hed_schema)
+    issue_list = []
+    for issue in issues:
+        code = issue.get('code', "")
+        message = issue.get('message', "")
+        column_name = issue.get(ErrorContext.SIDECAR_COLUMN_NAME, "")
+        if column_name:
+            column_name = column_name[0]
+        key_name = issue.get(ErrorContext.SIDECAR_KEY_NAME, "")
+        if key_name:
+            key_name = key_name[0]
+        hed_string = issue.get(ErrorContext.SIDECAR_HED_STRING, "")
+        issue_dict = {"code": code, "message": message, "column_name": column_name,
+                      "key_name": key_name, "hed_string": hed_string}
+        issue_list.append(issue_dict)
+    print(issue_list)
\ No newline at end of file
diff --git a/hedweb/tests/testing_dups.py b/hedweb/tests/testing_dups.py
index 12c65d9a5..f98286503 100644
--- a/hedweb/tests/testing_dups.py
+++ b/hedweb/tests/testing_dups.py
@@ -1,18 +1,27 @@
-import os
+import json
 import os.path
-from urllib.parse import urlparse
-from shutil import move
-from hed.schema import constants
-from hed.util.file_util import delete_file_if_it_exists, url_to_file, get_file_extension, write_text_iter_to_file
+
 
 if __name__ == '__main__':
     # hed_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data/HED.xml')
     # result_dict = duplicate_nodes.check_for_duplicate_tags(hed_file)
     # output_location = result_dict[constants.HED_OUTPUT_LOCATION_KEY]
     # move(output_location, "duplicate_tags_in_HEDXML.txt")
-    hed_file_path = ''
-    delete_file_if_it_exists(hed_file_path)
-    hed_file_path = None
-    delete_file_if_it_exists(hed_file_path)
-    url = urlparse('')
-    print(url)
\ No newline at end of file
+    # hed_file_path = ''
+    # delete_file_if_it_exists(hed_file_path)
+    # hed_file_path = None
+    # delete_file_if_it_exists(hed_file_path)
+    # url = urlparse('')
+    # print(url)
+    # x = bytearray("abcd", 'utf-8')
+    # y = x.decode()
+    # print(y)
+    #
+    dir_path = os.path.dirname(os.path.realpath(__file__))
+    the_path = os.path.join(dir_path, 'static/resources/services.json')
+    RESOURCE_PATH = './static/resources/services.json'
+    with open(the_path) as f:
+        service_info = json.load(f)
+
+    print(service_info)
+
diff --git a/hedweb/tests/testing_string_validate.py b/hedweb/tests/testing_string_validate.py
new file mode 100644
index 000000000..e6de29b13
--- /dev/null
+++ b/hedweb/tests/testing_string_validate.py
@@ -0,0 +1,26 @@
+import os.path
+from hed.validator.hed_validator import HedValidator
+from hed.util.error_reporter import get_printable_issue_string
+
+
+if __name__ == '__main__':
+
+    dir_path = os.path.dirname(os.path.realpath(__file__))
+    local_hed_file1 = os.path.join(dir_path, 'data/HED7.1.2.xml')
+    local_hed_file2 = os.path.join(dir_path, 'data/HED8.0.0-alpha.1.xml')
+
+    hed_validator_current = HedValidator(hed_xml_file=local_hed_file2)
+
+    # Example 1a: Valid HED string for HED <= v7.1.1
+    hed_string_1 = 'Event/Label/ButtonPuskDeny, Event/Description/Button push to deny access to the ID holder,' \
+                   'Event/Category/Participant response, ' \
+                   '(Participant ~ Action/Button press/Keyboard ~ Participant/Effect/Body part/Arm/Hand/Finger)'
+
+    validation_issues = hed_validator_current.validate_input(hed_string_1)
+    print(get_printable_issue_string(validation_issues, title='[Example 1b] hed_string_1 has issues with the latest HED version'))
+
+    hed_list = [hed_string_1, hed_string_1]
+
+    validation_issues = hed_validator_current.validate_input(hed_list)
+    print(get_printable_issue_string(validation_issues,
+                                                  title='[Example 1b] hed_string_1 has issues with the latest HED version'))
diff --git a/hedweb/tests/try_schema.py b/hedweb/tests/try_schema.py
new file mode 100644
index 000000000..ba1c453c0
--- /dev/null
+++ b/hedweb/tests/try_schema.py
@@ -0,0 +1,13 @@
+import os
+
+from hed.schema import hed_schema_file
+
+if __name__ == '__main__':
+    dir_path = os.path.dirname(os.path.realpath(__file__))
+    local_hed_xml = os.path.join(dir_path, 'data/HED8.0.0-alpha.1.xml')
+    local_hed_wiki = os.path.join(dir_path, 'data/HED3.mediawiki')
+
+    # Test conversion from xml to mediawiki
+    local_hed_schema = hed_schema_file.load_schema(local_hed_xml)
+    converted_wiki, errors_wiki = local_hed_schema.save_as_xml()
+    print('to here')