diff --git a/src/xml/xml_native_writer.cc b/src/xml/xml_native_writer.cc index 76917936..24f1a481 100644 --- a/src/xml/xml_native_writer.cc +++ b/src/xml/xml_native_writer.cc @@ -1057,6 +1057,13 @@ void mjXWriter::Default(XMLElement* root, mjCDef* def) { OneActuator(elem, &def->actuator, par); if (!elem->FirstAttribute()) section->DeleteChild(elem); + // if top-level class has no members or children, delete it and return + if (def->parentid<0 && section->NoChildren() && def->childid.empty()) { + root->DeleteChild(section); + delete par; + return; + } + // add children recursively for (int i=0; i<(int)def->childid.size(); i++) { Default(section, model->defaults[def->childid[i]]); diff --git a/test/xml/xml_native_writer_test.cc b/test/xml/xml_native_writer_test.cc index 7929c4f5..b125c9b5 100644 --- a/test/xml/xml_native_writer_test.cc +++ b/test/xml/xml_native_writer_test.cc @@ -52,6 +52,15 @@ using ::testing::NotNull; using XMLWriterTest = MujocoTest; +TEST_F(XMLWriterTest, EmptyModel) { + static constexpr char xml[] = ""; + mjModel* model = LoadModelFromString(xml); + ASSERT_THAT(model, NotNull()); + std::string saved_xml = SaveAndReadXml(model); + EXPECT_THAT(saved_xml, Not(HasSubstr("default"))); + mj_deleteModel(model); +} + TEST_F(XMLWriterTest, SavesMemory) { { static constexpr char xml[] = R"(