diff --git a/VirtualRobot/CMakeLists.txt b/VirtualRobot/CMakeLists.txt
index e636b335ac2db0cd985a27b4855554ee21aa3c9a..7427eeee2b2091fdfb18fecf9c6ff31189958313 100644
--- a/VirtualRobot/CMakeLists.txt
+++ b/VirtualRobot/CMakeLists.txt
@@ -328,6 +328,7 @@ SET(SOURCES
     MJCF/elements/core/Element.cpp
     MJCF/elements/core/exceptions.cpp
     MJCF/elements/core/mjcf_utils.cpp
+    MJCF/elements/core/Visitor.cpp
     
     Nodes/CameraSensor.cpp
     Nodes/CameraSensorFactory.cpp
@@ -534,6 +535,7 @@ SET(INCLUDES
     MJCF/elements/core/Element.h
     MJCF/elements/core/exceptions.h
     MJCF/elements/core/mjcf_utils.h
+    MJCF/elements/core/Visitor.h
     
     Nodes/CameraSensor.h
     Nodes/CameraSensorFactory.h
diff --git a/VirtualRobot/MJCF/elements/core/Visitor.cpp b/VirtualRobot/MJCF/elements/core/Visitor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..237cdd4e4acbac01466151f75937c1e179ac14a5
--- /dev/null
+++ b/VirtualRobot/MJCF/elements/core/Visitor.cpp
@@ -0,0 +1,48 @@
+#include "Visitor.h"
+
+#include "AnyElement.h"
+
+namespace mjcf
+{
+
+namespace detail
+{
+
+VisitorAdapter::VisitorAdapter(Visitor& owner) : 
+    owner(owner)
+{}
+
+bool VisitorAdapter::visitEnter(const tinyxml2::XMLElement& element)
+{
+    return owner.visitEnter(owner.cast(element));
+}
+
+bool VisitorAdapter::visitExit(const tinyxml2::XMLElement& element)
+{
+    return owner.visitExit(owner.cast(element));
+}
+
+}
+
+Visitor::Visitor(Document& document) : document(document)
+{}
+
+detail::VisitorAdapter* Visitor::adapter()
+{
+    return &_adapter;
+}
+
+const detail::VisitorAdapter* Visitor::adapter() const
+{
+    return &_adapter;
+}
+
+const AnyElement Visitor::cast(const tinyxml2::XMLElement& element)
+{
+    // The constructor of Element requires a non-const element pointer.
+    // As AnyElement is passed as const& to the user-provided visit method,
+    // this const cast should be sufficiently safe.
+    return { &document, const_cast<tinyxml2::XMLElement*>(&element) };
+}
+
+}
diff --git a/VirtualRobot/MJCF/elements/core/Visitor.h b/VirtualRobot/MJCF/elements/core/Visitor.h
new file mode 100644
index 0000000000000000000000000000000000000000..04a9dbd3a3e9a8f6aabe5b520d47ab5f1e594d74
--- /dev/null
+++ b/VirtualRobot/MJCF/elements/core/Visitor.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include <VirtualRobot/Util/xml/tinyxml2.h>
+
+
+namespace mjcf
+{
+    class AnyElement;
+    class Document;
+    class Visitor;
+
+namespace detail
+{
+
+    class VisitorAdapter : public tinyxml2::XMLVisitor
+    {
+    public:
+        VisitorAdapter(Visitor& owner);
+        virtual bool visitEnter(const tinyxml2::XMLElement& element);
+        virtual bool visitExit(const tinyxml2::XMLElement& element);
+        
+    private:
+        Visitor& owner;
+    };
+
+}
+
+    class Visitor
+    {
+    public:
+        
+        Visitor(Document& document);
+        virtual ~Visitor() = default;
+        
+        virtual bool visitEnter(const AnyElement&) { return true; }
+        virtual bool visitExit(const AnyElement&) { return true; }
+        
+        
+        detail::VisitorAdapter* adapter();
+        const detail::VisitorAdapter* adapter() const;
+        
+        
+    private:
+        
+        friend class detail::VisitorAdapter;
+        const AnyElement cast(const tinyxml2::XMLElement& element);
+        
+        
+        Document& document;
+        
+        detail::VisitorAdapter _adapter { *this };
+        
+    };
+
+}