diff --git a/odml/format.py b/odml/format.py
index bae2d68..7a0a796 100644
--- a/odml/format.py
+++ b/odml/format.py
@@ -130,7 +130,7 @@ class Section(Format):
     _args = {
         'id': 0,
         'type': 1,
-        'name': 0,
+        'name': 1,
         'definition': 0,
         'reference': 0,
         'link': 0,
diff --git a/odml/property.py b/odml/property.py
index 2602dea..f6d0211 100644
--- a/odml/property.py
+++ b/odml/property.py
@@ -13,7 +13,7 @@ class BaseProperty(base.BaseObject):
     """An odML Property"""
     _format = frmt.Property
 
-    def __init__(self, name, value=None, parent=None, unit=None,
+    def __init__(self, name=None, value=None, parent=None, unit=None,
                  uncertainty=None, reference=None, definition=None,
                  dependency=None, dependency_value=None, dtype=None,
                  value_origin=None, id=None):
@@ -58,6 +58,11 @@ class BaseProperty(base.BaseObject):
             print(e)
             self._id = str(uuid.uuid4())
 
+        # Use id if no name was provided.
+        if not name:
+            name = self._id
+
+        self._name = name
         self._parent = None
         self._name = name
         self._value_origin = value_origin
@@ -118,6 +123,14 @@ class BaseProperty(base.BaseObject):
 
     @name.setter
     def name(self, new_name):
+        if self.name == new_name:
+            return
+
+        curr_parent = self.parent
+        if hasattr(curr_parent, "properties") and new_name in curr_parent.properties:
+
+            raise KeyError("Object with the same name already exists!")
+
         self._name = new_name
 
     def __repr__(self):
diff --git a/odml/section.py b/odml/section.py
index fa08c1c..4707003 100644
--- a/odml/section.py
+++ b/odml/section.py
@@ -25,7 +25,7 @@ class BaseSection(base.Sectionable):
 
     _format = format.Section
 
-    def __init__(self, name, type=None, parent=None,
+    def __init__(self, name=None, type=None, parent=None,
                  definition=None, reference=None,
                  repository=None, link=None, include=None, id=None):
 
@@ -42,6 +42,10 @@ class BaseSection(base.Sectionable):
             print(e)
             self._id = str(uuid.uuid4())
 
+        # Use id if no name was provided.
+        if not name:
+            name = self._id
+
         self._parent = None
         self._name = name
         self._definition = definition
@@ -94,6 +98,13 @@ class BaseSection(base.Sectionable):
 
     @name.setter
     def name(self, new_value):
+        if self.name == new_value:
+            return
+
+        curr_parent = self.parent
+        if hasattr(curr_parent, "sections") and new_value in curr_parent.sections:
+            raise KeyError("Object with the same name already exists!")
+
         self._name = new_value
 
     @property
diff --git a/odml/tools/odmlparser.py b/odml/tools/odmlparser.py
index fbc7c71..2edd2e5 100644
--- a/odml/tools/odmlparser.py
+++ b/odml/tools/odmlparser.py
@@ -48,6 +48,10 @@ class ODMLWriter:
             raise ParserException(msg)
 
         with open(filename, 'w') as file:
+            # Add XML header to support odML stylesheets.
+            if self.parser == 'XML':
+                file.write(xmlparser.XMLWriter.header)
+
             file.write(self.to_string(odml_document))
 
     def to_string(self, odml_document):
diff --git a/odml/tools/xmlparser.py b/odml/tools/xmlparser.py
index f2ea862..c935c99 100644
--- a/odml/tools/xmlparser.py
+++ b/odml/tools/xmlparser.py
@@ -5,11 +5,11 @@ Parses odML files. Can be invoked standalone:
     python -m odml.tools.xmlparser file.odml
 """
 import csv
+import sys
 from lxml import etree as ET
 from lxml.builder import E
 # this is needed for py2exe to include lxml completely
 from lxml import _elementpath as _dummy
-import sys
 
 try:
     from StringIO import StringIO
@@ -118,10 +118,9 @@ class XMLWriter:
         else:
             data = str(self)
 
-        f = open(filename, "w")
-        f.write(self.header)
-        f.write(data)
-        f.close()
+        with open(filename, "w") as file:
+            file.write(self.header)
+            file.write(data)
 
 
 def load(filename):
@@ -223,18 +222,20 @@ class XMLReader(object):
             return None  # won't be able to parse this one
         return getattr(self, "parse_" + node.tag)(node, self.tags[node.tag])
 
-    def parse_tag(self, root, fmt, insert_children=True, create=None):
+    def parse_tag(self, root, fmt, insert_children=True):
         """
         Parse an odml node based on the format description *fmt*
-        and a function *create* to instantiate a corresponding object
+        and instantiate the corresponding object.
+        :param root: lxml.etree node containing an odML object or object tree.
+        :param fmt: odML class corresponding to the content of the root node.
+        :param insert_children: Bool value. When True, child elements of the root node
+                                will be parsed to their odML equivalents and appended to
+                                the odML document. When False, child elements of the
+                                root node will be ignored.
         """
         arguments = {}
         extra_args = {}
         children = []
-        text = []
-
-        if root.text:
-            text.append(root.text.strip())
 
         for k, v in root.attrib.iteritems():
             k = k.lower()
@@ -258,8 +259,6 @@ class XMLReader(object):
                 else:
                     tag = fmt.map(node.tag)
                     if tag in arguments:
-                        # TODO make this an error, however first figure out a
-                        # way to let <odML version=><version/> pass
                         self.warn("Element <%s> is given multiple times in "
                                   "<%s> tag" % (node.tag, root.tag), node)
 
@@ -273,38 +272,21 @@ class XMLReader(object):
             else:
                 self.error("Invalid element <%s> in odML document section <%s>"
                            % (node.tag, root.tag), node)
-            if node.tail:
-                text.append(node.tail.strip())
 
         if sys.version_info > (3,):
-            self.check_mandatory_arguments(dict(list(arguments.items()) +
-                                           list(extra_args.items())),
-                                           fmt, root.tag, root)
+            check_args = dict(list(arguments.items()) + list(extra_args.items()))
         else:
-            self.check_mandatory_arguments(dict(arguments.items() +
-                                           extra_args.items()),
-                                           fmt, root.tag, root)
-        if create is None:
-            obj = fmt.create()
-        else:
-            obj = create(args=arguments, text=''.join(text), children=children)
+            check_args = dict(arguments.items() + extra_args.items())
 
-        for k, v in arguments.items():
-            if hasattr(obj, k) and (getattr(obj, k) is None or k == 'id'):
-                try:
-                    if k == 'id' and v is not None:
-                        obj._id = v
-                    else:
-                        setattr(obj, k, v)
-                except Exception as e:
-                    self.warn("cannot set '%s' property on <%s>: %s" %
-                              (k, root.tag, repr(e)), root)
-                    if not self.ignore_errors:
-                        raise e
+        self.check_mandatory_arguments(check_args, fmt, root.tag, root)
+
+        # Instantiate the current odML object with the parsed attributes.
+        obj = fmt.create(**arguments)
 
         if insert_children:
             for child in children:
                 obj.append(child)
+
         return obj
 
     def parse_odML(self, root, fmt):
@@ -312,24 +294,10 @@ class XMLReader(object):
         return doc
 
     def parse_section(self, root, fmt):
-        name = root.get("name")  # property name= overrides
-        if name is None:  # the element
-            name_node = root.find("name")
-            if name_node is not None:
-                name = name_node.text
-                root.remove(name_node)
-                # delete the name_node so its value won't
-                # be used to overwrite the already set name-attribute
-
-        if name is None:
-            self.error("Missing name element in <section>", root)
-
-        return self.parse_tag(root, fmt,
-                              create=lambda **kargs: fmt.create(name))
+        return self.parse_tag(root, fmt)
 
     def parse_property(self, root, fmt):
-        create = lambda children, args, **kargs: fmt.create(**args)
-        return self.parse_tag(root, fmt, insert_children=False, create=create)
+        return self.parse_tag(root, fmt, insert_children=False)
 
 
 if __name__ == '__main__':

odML Format update
Define Section `name` and `type` as well as Property `name` as required in `format.py`.
diff --git a/test/test_property.py b/test/test_property.py
index 9138cae..9eafedb 100644
--- a/test/test_property.py
+++ b/test/test_property.py
@@ -327,6 +327,40 @@ class TestProperty(unittest.TestCase):
         assert(p.dtype == 'string')
         assert(p.value == ['7', '20', '1 Dog', 'Seven'])
 
+    def test_name(self):
+        # Test id is used when name is not provided
+        p = Property()
+        self.assertIsNotNone(p.name)
+        self.assertEqual(p.name, p.id)
+
+        # Test name is properly set on init
+        name = "rumpelstilzchen"
+        p = Property(name)
+        self.assertEqual(p.name, name)
+
+        # Test name can be properly set on single and connected Properties
+        prop = Property()
+        self.assertNotEqual(prop.name, "prop")
+        prop.name = "prop"
+        self.assertEqual(prop.name, "prop")
+
+        sec = Section()
+        prop_a = Property(parent=sec)
+        self.assertNotEqual(prop_a.name, "prop_a")
+        prop_a.name = "prop_a"
+        self.assertEqual(prop_a.name, "prop_a")
+
+        # Test property name can be changed with siblings
+        prop_b = Property(name="prop_b", parent=sec)
+        self.assertEqual(prop_b.name, "prop_b")
+        prop_b.name = "prop"
+        self.assertEqual(prop_b.name, "prop")
+
+        # Test property name set will fail on existing sibling with same name
+        with self.assertRaises(KeyError):
+            prop_b.name = "prop_a"
+        self.assertEqual(prop_b.name, "prop")
+
     def test_parent(self):
         p = Property("property_section", parent=Section("S"))
         self.assertIsInstance(p.parent, BaseSection)
diff --git a/test/test_section.py b/test/test_section.py
index 84604aa..5581928 100644
--- a/test/test_section.py
+++ b/test/test_section.py
@@ -39,6 +39,50 @@ class TestSection(unittest.TestCase):
         sec.definition = ""
         self.assertIsNone(sec.definition)
 
+    def test_name(self):
+        # Test id is used when name is not provided
+        s = Section()
+        self.assertIsNotNone(s.name)
+        self.assertEqual(s.name, s.id)
+
+        # Test name is properly set on init
+        name = "rumpelstilzchen"
+        s = Section(name)
+        self.assertEqual(s.name, name)
+
+        name = "rumpelstilzchen"
+        s = Section(name=name)
+        self.assertEqual(s.name, name)
+
+        # Test name can be properly set on single and connected Sections
+        sec = Section()
+        self.assertNotEqual(sec.name, "sec")
+        sec.name = "sec"
+        self.assertEqual(sec.name, "sec")
+
+        subsec_a = Section(parent=sec)
+        self.assertNotEqual(subsec_a.name, "subsec_a")
+        subsec_a.name = "subsec_a"
+        self.assertEqual(subsec_a.name, "subsec_a")
+
+        # Test subsection name can be changed with siblings
+        subsec_b = Section(name="subsec_b", parent=sec)
+        self.assertEqual(subsec_b.name, "subsec_b")
+        subsec_b.name = "subsec"
+        self.assertEqual(subsec_b.name, "subsec")
+
+        # Test subsection name set will fail on existing sibling with same name
+        with self.assertRaises(KeyError):
+            subsec_b.name = "subsec_a"
+        self.assertEqual(subsec_b.name, "subsec")
+
+        # Test section name set will fail on existing same name document sibling
+        doc = Document()
+        sec_a = Section(name="a", parent=doc)
+        sec_b = Section(name="b", parent=doc)
+        with self.assertRaises(KeyError):
+            sec_b.name = "a"
+
     def test_parent(self):
         s = Section("Section")
         self.assertIsNone(s.parent)

pip install -e .
parse_log_pytest
pytest
pytest
apt-get update apt-get install -y libxml2-dev libxslt1-dev lib32z1-dev
3.9
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
exceptiongroup @ file:///croot/exceptiongroup_1706031385326/work
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
isodate==0.7.2
lxml==5.3.1
-e git+https://github.com/G-Node/python-odml.git@bc4bade4c93e0d5cb3ab8c0fb427fcf3c0ed96e1#egg=odML
packaging @ file:///croot/packaging_1734472117206/work
pluggy @ file:///croot/pluggy_1733169602837/work
pyparsing==3.2.3
pytest @ file:///croot/pytest_1738938843180/work
PyYAML==6.0.2
rdflib==7.1.4
tomli @ file:///opt/conda/conda-bld/tomli_1657175507142/work

name: python-odml
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - exceptiongroup=1.2.0=py39h06a4308_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - packaging=24.2=py39h06a4308_0
  - pip=25.0=py39h06a4308_0
  - pluggy=1.5.0=py39h06a4308_0
  - pytest=8.3.4=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tomli=2.0.1=py39h06a4308_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - isodate==0.7.2
      - lxml==5.3.1
      - pyparsing==3.2.3
      - pyyaml==6.0.2
      - rdflib==7.1.4
prefix: /opt/conda/envs/python-odml

test/test_property.py::TestProperty::test_name test/test_section.py::TestSection::test_name
test/test_property.py::TestProperty::test_bool_conversion test/test_property.py::TestProperty::test_clone test/test_property.py::TestProperty::test_dtype test/test_property.py::TestProperty::test_get_merged_equivalent test/test_property.py::TestProperty::test_get_path test/test_property.py::TestProperty::test_get_set_value test/test_property.py::TestProperty::test_id test/test_property.py::TestProperty::test_merge test/test_property.py::TestProperty::test_merge_check test/test_property.py::TestProperty::test_new_id test/test_property.py::TestProperty::test_parent test/test_property.py::TestProperty::test_simple_attributes test/test_property.py::TestProperty::test_str_to_int_convert test/test_property.py::TestProperty::test_value test/test_property.py::TestProperty::test_value_append test/test_property.py::TestProperty::test_value_extend test/test_section.py::TestSection::test_append test/test_section.py::TestSection::test_children test/test_section.py::TestSection::test_clone test/test_section.py::TestSection::test_contains test/test_section.py::TestSection::test_extend test/test_section.py::TestSection::test_id test/test_section.py::TestSection::test_include test/test_section.py::TestSection::test_insert test/test_section.py::TestSection::test_link test/test_section.py::TestSection::test_merge test/test_section.py::TestSection::test_merge_check test/test_section.py::TestSection::test_new_id test/test_section.py::TestSection::test_parent test/test_section.py::TestSection::test_path test/test_section.py::TestSection::test_remove test/test_section.py::TestSection::test_reorder test/test_section.py::TestSection::test_repository test/test_section.py::TestSection::test_simple_attributes test/test_section.py::TestSection::test_unmerge
odml/property.py odml/format.py odml/tools/odmlparser.py odml/tools/xmlparser.py odml/section.py
odml/property.py odml/format.py odml/tools/odmlparser.py odml/tools/xmlparser.py odml/section.py