# How to Load and Parse URDF, SDF, MJCF, and SKEL Files Using DART's IO Module

> Easily load and parse URDF SDF MJCF and SKEL files with DARTs io module. The unified API automatically detects file formats simplifying your robotics workflow.

- Repository: [DART: Dynamic Animation and Robotics Toolkit/dart](https://github.com/dartsim/dart)
- Tags: how-to-guide
- Published: 2026-02-28

---

**DART provides a unified `dart::io` API that automatically detects and parses URDF, SDF, MJCF, and SKEL files through the `readWorld()` and `readSkeleton()` functions, eliminating the need to manually select format-specific parsers.**

The `dartsim/dart` robotics toolkit consolidates robot description loading into a single interface located in [`dart/io/read.hpp`](https://github.com/dartsim/dart/blob/main/dart/io/read.hpp). Whether you are importing a URDF robot model, an SDF simulation world, a MuJoCo MJCF file, or DART's native SKEL format, the IO module handles format inference, resource retrieval, and parser dispatch automatically.

## Unified IO API Overview

DART's modern IO layer centers on two high-level functions defined in **[`dart/io/read.hpp`](https://github.com/dartsim/dart/blob/main/dart/io/read.hpp)** and implemented in **[`dart/io/read.cpp`](https://github.com/dartsim/dart/blob/main/dart/io/read.cpp)**:

- `readWorld(const common::Uri&, const ReadOptions&)` – returns a `simulation::WorldPtr`
- `readSkeleton(const common::Uri&, const ReadOptions&)` – returns a `dynamics::SkeletonPtr`

Both functions follow a three-stage pipeline implemented in `readWorld` (lines 40-57). First, `resolveOptions()` normalizes the `ReadOptions` object, ensuring a valid `ResourceRetriever` exists and collecting package directories. Second, `inferFormat()` determines the file type by examining the extension via `inferFormatFromExtension()` or parsing the XML root element via `inferFormatFromXmlRoot()` (lines 96-121). Finally, a `switch` statement dispatches to the appropriate concrete parser based on the resolved `ModelFormat` enum.

## Supported File Formats and Parsers

DART supports four major description formats through dedicated parser classes:

| Format | Extension / Root Element | Parser Class |
|--------|-------------------------|--------------|
| **SKEL** | `.skel` (`<skel>`) | `utils::SkelParser` |
| **SDF** | `.sdf` / `.world` (`<sdf>`) | `utils::SdfParser` |
| **URDF** | `.urdf` (`<robot>`) | `utils::UrdfParser` |
| **MJCF** | `.xml` (`<mujoco>`) | `utils::MjcfParser` |

The dispatch logic in [`read.cpp`](https://github.com/dartsim/dart/blob/main/read.cpp) (lines 57-95) routes requests to these parsers while handling format-specific configuration such as URDF package resolution and SDF default root joint types.

## How Format Detection Works

When you pass `ModelFormat::Auto` (the default), DART employs a two-tier inference strategy:

1. **Extension Analysis**: `inferFormatFromExtension()` maps file suffixes (`.skel`, `.sdf`, `.urdf`, `.mjcf`) to their respective formats.
2. **XML Root Inspection**: If the extension is ambiguous (e.g., `.xml`), `inferFormatFromXmlRoot()` uses **tinyxml2** to examine the root tag—`<skel>`, `<sdf>`, `<robot>`, or `<mujoco>`—and identifies the correct parser.

You can bypass automatic detection by explicitly setting the format in `ReadOptions`:

```cpp
dart::io::ReadOptions opt;
opt.format = dart::io::ModelFormat::Urdf;

```

## Loading Worlds and Skeletons

### Auto-Detecting File Format

The simplest invocation relies on automatic format inference to load any supported file type:

```cpp
#include <dart/io/read.hpp>

int main()
{
  const dart::common::Uri uri("file:///path/to/model.sdf");
  
  // Auto-detects format and returns a WorldPtr
  auto world = dart::io::readWorld(uri);
  
  if (!world) {
    std::cerr << "Failed to load world!\n";
    return 1;
  }
  
  std::cout << "World contains " << world->getNumSkeletons() 
            << " skeleton(s).\n";
}

```

### Loading URDF with Package Directories

URDF files frequently reference resources via `package://` URIs. DART resolves these through the `PackageResourceRetriever` configured via `ReadOptions::addPackageDirectory()`:

```cpp
#include <dart/io/read.hpp>

int main()
{
  dart::io::ReadOptions opt;
  opt.format = dart::io::ModelFormat::Urdf;
  opt.addPackageDirectory("my_robot", "/home/user/robot_pkg");
  
  const dart::common::Uri uri("file:///home/user/robot_pkg/robot.urdf");
  auto skeleton = dart::io::readSkeleton(uri, opt);
  
  if (!skeleton) {
    std::cerr << "Could not read URDF skeleton.\n";
    return 1;
  }
  
  std::cout << "Skeleton name: " << skeleton->getName() << '\n';
}

```

The `getUrdfResourceRetriever()` function (lines 191-212 in [`read.cpp`](https://github.com/dartsim/dart/blob/main/read.cpp)) constructs a resource retriever chain that intercepts `package://` schemes and maps them to the directories specified in `opt.urdfPackageDirectories`.

### Loading SDF with Custom Root Joint Types

By default, SDF models receive floating root joints. You can enforce fixed joints via `ReadOptions`:

```cpp
#include <dart/io/read.hpp>

int main()
{
  dart::io::ReadOptions opt;
  opt.sdfDefaultRootJointType = dart::io::RootJointType::Fixed;
  
  const dart::common::Uri uri("file:///my/sim.world");
  auto world = dart::io::readWorld(uri, opt);
}

```

The translation from `io::RootJointType` to `utils::SdfParser::RootJointType` occurs inside `readWorld` (lines 262-268).

### Loading MJCF Models

MuJoCo XML (MJCF) files are treated as world descriptions. Access individual skeletons through the returned world pointer:

```cpp
#include <dart/io/read.hpp>

int main()
{
  const dart::common::Uri uri("file:///tmp/mjcf_model.xml");
  
  // MJCF loads as a world containing the model
  auto world = dart::io::readWorld(uri);
  auto skel = world->getSkeleton(0);
}

```

`readWorld` dispatches to `utils::MjcfParser::readWorld` (lines 77-78 in [`read.cpp`](https://github.com/dartsim/dart/blob/main/read.cpp)).

### Error Handling with tryReadWorld

For exception-free error handling, use the result-based API that wraps `readWorld`:

```cpp
#include <dart/io/read.hpp>
#include <iostream>

int main()
{
  const dart::common::Uri uri("file:///missing/file.skel");
  
  auto result = dart::io::tryReadWorld(uri);
  if (!result) {
    std::cerr << "Error: " << result.error().what() << '\n';
    return 1;
  }
  
  auto world = result.value();
  std::cout << "Loaded world with " << world->getNumSkeletons() 
            << " skeleton(s).\n";
}

```

`tryReadWorld` returns a `common::Result` object containing either the `WorldPtr` or an error message.

## Key Classes and Configuration

**`ReadOptions`** (defined in [`dart/io/read.hpp`](https://github.com/dartsim/dart/blob/main/dart/io/read.hpp), lines 85-108) serves as the configuration container:

- `format` – Explicit `ModelFormat` enum value or `Auto`
- `resourceRetriever` – Custom `common::ResourceRetriever` for network or embedded resources
- `urdfPackageDirectories` – Map of package names to filesystem paths
- `sdfDefaultRootJointType` – Specifies fixed or floating root joints for SDF

**`common::ResourceRetriever`** abstracts file access, enabling support for `file://`, `dart://`, and `package://` schemes through a composite retriever pattern defined in [`dart/common/resource_retriever.hpp`](https://github.com/dartsim/dart/blob/main/dart/common/resource_retriever.hpp).

## Summary

- **Unified API**: Use `dart::io::readWorld()` and `dart::io::readSkeleton()` from [`dart/io/read.hpp`](https://github.com/dartsim/dart/blob/main/dart/io/read.hpp) to load any supported format.
- **Automatic Detection**: DART infers file types from extensions (`.urdf`, `.sdf`, `.skel`, `.mjcf`) or XML root elements (`<robot>`, `<sdf>`, `<skel>`, `<mujoco>`).
- **URDF Packages**: Configure `package://` resolution via `ReadOptions::addPackageDirectory()`.
- **SDF Configuration**: Control root joint behavior with `ReadOptions::sdfDefaultRootJointType`.
- **Error Handling**: Use `tryReadWorld()` or `tryReadSkeleton()` for result-based error propagation instead of exceptions.

## Frequently Asked Questions

### How does DART determine which parser to use for a file?

DART examines the file extension first through `inferFormatFromExtension()`; if the extension is generic (like `.xml`), it parses the XML root element using `inferFormatFromXmlRoot()` to identify whether the document contains `<robot>`, `<sdf>`, `<skel>`, or `<mujoco>` tags. This logic resides in [`dart/io/read.cpp`](https://github.com/dartsim/dart/blob/main/dart/io/read.cpp) lines 96-121.

### Can I force DART to use a specific parser instead of auto-detecting?

Yes. Set the `format` field in your `ReadOptions` object to a specific `ModelFormat` enum value such as `ModelFormat::Urdf`, `ModelFormat::Sdf`, `ModelFormat::Mjcf`, or `ModelFormat::Skel` before calling `readWorld()` or `readSkeleton()`.

### Why does my URDF file fail to load resources using `package://` URIs?

The default resource retriever does not resolve `package://` schemes. You must populate `ReadOptions::urdfPackageDirectories` using `addPackageDirectory()` to map package names to absolute filesystem paths. The IO module then constructs a `PackageResourceRetriever` (lines 191-212 in [`read.cpp`](https://github.com/dartsim/dart/blob/main/read.cpp)) to handle these requests.

### Does DART support loading individual skeletons from MJCF files?

No. The MJCF parser (`utils::MjcfParser`) only exposes a world-loading interface. You must call `readWorld()` and then extract skeletons using `world->getSkeleton(index)` or iterate through all skeletons in the returned world.