6#include <libxml/xmlsave.h>
25 if (!tree->HasRootNode()) {
30 LOG_INFO(
"LibXMLSerializer",
"Serializing tree to file: " + filepath);
41 int result = xmlSaveFormatFileEnc(filepath.c_str(), doc,
"UTF-8", 1);
51 LOG_INFO(
"LibXMLSerializer",
"Successfully serialized tree to: " + filepath);
63 if (!tree->HasRootNode()) {
76 xmlChar *xml_buffer =
nullptr;
78 xmlDocDumpFormatMemoryEnc(doc, &xml_buffer, &buffer_size,
"UTF-8", 1);
82 result =
reinterpret_cast<const char *
>(xml_buffer);
93 const auto &doc_config =
config_.GetDocumentConfig();
96 xmlDocPtr doc = xmlNewDoc(BAD_CAST
"1.0");
102 xmlNodePtr root = xmlNewNode(
nullptr, BAD_CAST doc_config.root_element.c_str());
103 xmlDocSetRootElement(doc, root);
107 if (!tree_name.empty()) {
108 xmlNewProp(root, BAD_CAST doc_config.main_tree_attribute.c_str(), BAD_CAST tree_name.c_str());
112 const auto &xml_metadata = tree->GetXMLMetadata();
113 for (
const auto &comment : xml_metadata.header_comments) {
114 xmlNodePtr comment_node = xmlNewComment(BAD_CAST comment.c_str());
115 xmlAddPrevSibling(root, comment_node);
122 for (
const auto &comment : xml_metadata.footer_comments) {
123 xmlNodePtr comment_node = xmlNewComment(BAD_CAST comment.c_str());
124 xmlAddSibling(root, comment_node);
131 const auto &tree_config =
config_.GetTreeConfig();
134 xmlNodePtr tree_node = xmlNewChild(parent,
nullptr, BAD_CAST tree_config.behavior_tree_element.c_str(),
nullptr);
138 if (tree_name.empty()) {
139 tree_name =
"MainTree";
141 xmlNewProp(tree_node, BAD_CAST tree_config.tree_id_attribute.c_str(), BAD_CAST tree_name.c_str());
147 Node *root = tree->GetRootNode();
164 const auto &blackboards = tree->GetBlackboards();
165 for (
const auto &bb_pair : blackboards) {
181 xmlNodePtr xml_node = xmlNewChild(parent,
nullptr, BAD_CAST element_name.c_str(),
nullptr);
183 const auto &node_config =
config_.GetNodeConfig();
184 const auto &tree_config =
config_.GetTreeConfig();
188 if (!subtree_ref.empty()) {
190 xmlNewProp(xml_node, BAD_CAST tree_config.subtree_reference_attribute.c_str(), BAD_CAST subtree_ref.c_str());
196 if (!node_config.node_id_attribute.empty()) {
200 if (!node_type_id.empty()) {
201 xmlNewProp(xml_node, BAD_CAST node_config.node_id_attribute.c_str(), BAD_CAST node_type_id.c_str());
205 LOG_WARNING(
"LibXMLSerializer",
"Node '" + node->
GetName() +
"' has no ID attribute - skipping");
210 xmlNewProp(xml_node, BAD_CAST node_config.node_name_attribute.c_str(), BAD_CAST node->
GetName().c_str());
227 const auto &node_config =
config_.GetNodeConfig();
232 for (
const auto &attr_pair : attributes) {
234 if (attr_pair.first == node_config.node_name_attribute || attr_pair.first == node_config.node_id_attribute) {
239 if (attr_pair.first ==
"__subtree_ref__" || attr_pair.first ==
"__is_placeholder__") {
244 xmlNewProp(xml_node, BAD_CAST attr_pair.first.c_str(), BAD_CAST attr_pair.second.c_str());
253 const auto &blackboard_config =
config_.GetBlackboardConfig();
256 xmlNodePtr bb_node = xmlNewChild(parent,
nullptr, BAD_CAST blackboard_config.blackboard_element.c_str(),
nullptr);
259 xmlNewProp(bb_node, BAD_CAST blackboard_config.blackboard_id_attribute.c_str(),
260 BAD_CAST blackboard->
GetId().c_str());
263 const auto &entries = blackboard->
GetEntries();
264 for (
const auto &entry_pair : entries) {
265 xmlNodePtr entry_node =
266 xmlNewChild(bb_node,
nullptr, BAD_CAST blackboard_config.entry_element.c_str(),
nullptr);
268 xmlNewProp(entry_node, BAD_CAST blackboard_config.entry_key_attribute.c_str(),
269 BAD_CAST entry_pair.first.c_str());
270 xmlNewProp(entry_node, BAD_CAST blackboard_config.entry_type_attribute.c_str(),
271 BAD_CAST entry_pair.second->GetTypeString().c_str());
275 if (!value.empty()) {
276 xmlNewProp(entry_node, BAD_CAST blackboard_config.entry_value_attribute.c_str(), BAD_CAST value.c_str());
284 errors_.emplace_back(type, message);
293 const auto &node_config =
config_.GetNodeConfig();
294 const auto &tree_config =
config_.GetTreeConfig();
299 if (!subtree_ref.empty()) {
300 return tree_config.subtree_element;
303 LOG_WARNING(
"LibXMLSerializer",
"Unexpected BehaviorTree node in serialization: " + node->
GetName());
304 return node_config.generic_node_element;
311 return node_config.action_element;
313 return node_config.control_element;
315 return node_config.condition_element;
317 return node_config.decorator_element;
319 return node_config.generic_node_element;
#define LOG_ERROR(category, message)
#define LOG_WARNING(category, message)
#define LOG_INFO(category, message)
Represents a blackboard containing multiple entries.
const EmberCore::String & GetId() const
const std::map< EmberCore::String, std::unique_ptr< BlackboardEntry > > & GetEntries() const
void SerializeAttributes(Node *node, xmlNodePtr xml_node)
Serialize node attributes.
LibXMLBehaviorTreeSerializer(const ParserConfig &config)
Constructor with parser configuration.
~LibXMLBehaviorTreeSerializer()
Destructor.
std::vector< SerializeError > errors_
EmberCore::String SerializeToString(std::shared_ptr< BehaviorTree > tree)
Serialize behavior tree to string.
EmberCore::String GetElementNameForNode(Node *node) const
Get node element name based on node type and config.
xmlDocPtr CreateXMLDocument(std::shared_ptr< BehaviorTree > tree)
Create XML document from behavior tree.
xmlNodePtr SerializeBehaviorTree(std::shared_ptr< BehaviorTree > tree, xmlNodePtr parent)
Serialize a single behavior tree to XML node.
xmlNodePtr SerializeNode(Node *node, xmlNodePtr parent)
Recursively serialize a node and its children.
bool SerializeToFile(std::shared_ptr< BehaviorTree > tree, const EmberCore::String &filepath)
Serialize behavior tree to file.
void AddError(SerializeError::Type type, const EmberCore::String &message)
Error handling.
xmlNodePtr SerializeBlackboard(Blackboard *blackboard, xmlNodePtr parent)
Serialize blackboard.
Represents a node in a behavior tree structure.
const std::map< String, String > & GetAllAttributes() const
String GetAttribute(const String &name, const String &default_value="") const
Node * GetChild(size_t index) const
const String & GetName() const
size_t GetChildCount() const
Configuration for XML parser behavior and element/attribute mappings.
Main types header for EmberCore.
std::string String
Framework-agnostic string type.