自定義Cereal XML輸出容器節點

自定義Cereal XML輸出容器節點

CEREAL_SERIALIZE_INTRUSIVE 在 1.優化Cereal宏 一行聲明序列化函數

QString、QVector、QList、QMap序列化在2.在Cereal中支持Qt容器序列化

靜態成員函數type_node檢測在 3.利用SFINAE檢測成員函數

🚀 告別value0:自定義Cereal序列化節點名稱的終極方案

—— 讓XML輸出語義化,告別自動生成的冗余標簽


🔧 問題背景:默認序列化的痛點

使用Cereal序列化Qt容器(如QVector<Animal>)時,XML默認生成<value0><value1>等無意義節點:

沒改之前,節點是value0…輸出為

<?xml version="1.0" encoding="utf-8"?>
<cereal><zoo size="dynamic"><value0><!-- 無意義標簽 --><age>1</age><type>1</type><name>a1</name><master>m1</master></value0><value1><age>2</age><type>2</type><name>b2</name><master>m2</master></value1><value2>.........................................................................</zoo>
</cereal>

這種設計導致:

  1. 可讀性差:節點名無法反映數據結構含義
  2. 處理困難:XPath查詢需依賴順序而非語義

改完注冊,節點不再是value0…,輸出

<?xml version="1.0" encoding="utf-8"?>
<cereal><zoo size="dynamic"><Animal><age>1</age><type>1</type><name>a1</name><master>m1</master></Animal><Animal><age>2</age><type>2</type><name>b2</name><master>m2</master></Animal><Animal>.........................................................................</zoo>
</cereal>

?? 解決方案:動態注冊自定義節點名

通過編譯期類型檢測 + SFINAE模板特化,實現節點名稱的語義化替換:

// 1. 定義類型注冊宏
#define REGISTER_TYPE_NODE(Type) \static const char* type_node(){ return #Type; };// 2. 檢測類型是否注冊
template<class T>
struct has_static_member_type_node {template<class TT>static auto test(int) -> decltype(std::decay_t<TT>::type_node(), std::true_type{});template<class> static std::false_type test(...);static constexpr bool value = decltype(test<T>(0))::value;
};// 3. 根據注冊狀態動態設置節點名
template <class Archive, typename T>
typename std::enable_if<has_static_member_type_node<T>::value, void>::type
customNodeName(Archive& ar, const T& t) {ar.setNextName(std::decay_t<T>::type_node()); // 使用注冊名
}

🧠 關鍵技術解析
技術點作用優勢
REGISTER_TYPE_NODE為類型注入type_node()靜態函數非侵入式,無需修改原結構定義
SFINAE檢測編譯期判斷類型是否包含type_node()零運行時開銷4,6
ar.setNextName()覆蓋Cereal默認節點命名邏輯精準控制XML輸出結構

💻 完整實現:Qt容器+自定義類型適配

1. 自定義類型注冊示例
struct Animal {int age, type;QString name, master;// 序列化成員CEREAL_SERIALIZE_INTRUSIVE(age, type, name, master)// 關鍵!注冊XML節點名REGISTER_TYPE_NODE(Animal) // 輸出標簽變為<Animal>
};
2. Qt容器序列化改造
namespace cereal {template <class Archive, typename T>typename std::enable_if<!Archive::is_loading::value>::typeserialize(Archive& ar, QVector<T>& vec) {ar(make_size_tag(vec.size()));for (auto&& item : vec) {customNodeName(ar, item); // 動態設置節點名ar(item); // 正常序列化}}
}
3. QString優化(避免二次嵌套)
void CEREAL_SAVE_FUNCTION_NAME(XMLOutputArchive& ar, QString const& str) {ar.saveValue(str.toStdString()); // 直接輸出文本值
}

? 效果對比

修改前

<value0> <!-- 無意義標簽 --><age>1</age><name>a1</name>
</value0>

修改后

<Animal> <!-- 語義化標簽 --><age>1</age><name>a1</name>
</Animal>

優化收益

  • 可讀性↑:節點名直接反映數據類型
  • 兼容性:完全兼容Cereal反序列化邏輯

🧪 測試驗證

// 序列化測試
QList<Animal> animals = {{1,1,"a1","m1"}, {2,2,"b2","m2"}};
cereal::XMLOutputArchive archive(oss);
archive(cereal::make_nvp("zoo", animals));// 輸出結果
std::cout << oss.str();

輸出驗證

<zoo size="dynamic"><Animal> <!-- 自定義節點名 --><age>1</age><type>1</type><name>a1</name></Animal><Animal><age>2</age><type>2</type><name>b2</name></Animal>
</zoo>

💎 總結

通過 REGISTER_TYPE_NODE+customNodeName+SFINAE檢測 的三段式設計,實現了:

  1. 零侵入改造:不修改原有類結構
  2. 編譯期優化:無運行時性能損失
  3. 格式統一:輸出語義化XML,提升數據處理效率

最佳實踐:建議結合#define CEREAL_XML_STRING_VALUE "root"使用,實現全鏈路節點控制


技術改變輸出,細節決定體驗。讓序列化結果不再是一堆冰冷的value0標簽,而是充滿業務語義的數據藍圖。

核心代碼

CEREAL_SERIALIZE_INTRUSIVE 在 1.優化Cereal宏 一行聲明序列化函數

QString、QVector、QList、QMap序列化在2.在Cereal中支持Qt容器序列化

靜態成員函數type_node檢測在 3.利用SFINAE檢測成員函數

#pragma region 節點不再是value0...
#define REGISTER_TYPE_NODE(Type) static const char* type_node(){ return #Type;};// 靜態成員函數type_node檢測
template<class T>
struct has_static_member_type_node {template<class TT>static auto test(int) -> decltype(std::decay_t<TT>::type_node(), std::true_type{});template<class>static std::false_type test(...) {};static constexpr bool value = decltype(test<T>(0))::value;
};template <class Archive, typename T>
typename std::enable_if<has_static_member_type_node<T>::value, void>::type
customNodeName(Archive& ar, const T& t)
{ar.setNextName(std::decay_t<T>::type_node());
}template <class Archive, typename T>
typename std::enable_if<!has_static_member_type_node<T>::value, void>::type
customNodeName(Archive& ar, const T& t)
{// default in xml.hpp std::string getValueName()
}
#pragma endregion namespace cereal
{// 避免XML中生成<value0>節點//! saving string to xmlvoid CEREAL_SAVE_FUNCTION_NAME(XMLOutputArchive& ar, QString const& str){ar.saveValue(str.toStdString());}//! loading string from xmlvoid CEREAL_LOAD_FUNCTION_NAME(XMLInputArchive& ar, QString& str){std::string temp;ar.loadValue(temp);str = QString::fromStdString(temp);}// 保存 QVectortemplate <class Archive, typename T>typename std::enable_if<!Archive::is_loading::value, void>::typeserialize(Archive& ar, QVector<T>& vec) {ar(make_size_tag(vec.size())); // number of elementsfor (auto&& item : vec) {
#pragma region 節點不再是value0...customNodeName(ar, item);
#pragma endregion ar(item);}}// 加載 QVectortemplate <class Archive, typename T>typename std::enable_if<Archive::is_loading::value, void>::typeserialize(Archive& ar, QVector<T>& vec) {size_type size;ar(make_size_tag(size));vec.resize(size);for (auto&& item : vec) {ar(item);}}
};// 動物
struct Animal
{int age = 0;int type = 0;QString name;QString master;Animal() = default;Animal(int a,int t,QString n,QString m):age(a), type(t), name(n), master(m){};Animal(const Animal&) = default;Animal(Animal&&) = default;CEREAL_SERIALIZE_INTRUSIVE(age, type, name, master)
#pragma region 節點不再是value0...REGISTER_TYPE_NODE(Animal)
#pragma endregion 
};

測試代碼

QList<Animal> list_Animal;
list_Animal.push_back(Animal(1, 1, "a1", "m1"));
list_Animal.push_back(Animal(2, 2, "b2", "m2"));
list_Animal.push_back(Animal(3, 3, "c3", "m3"));
list_Animal.push_back(Animal(4, 4, "d4", "m4"));
list_Animal.push_back(Animal(5, 5, "e5", "m5"));std::ostringstream oss2; // 內存輸出流
{cereal::XMLOutputArchive archiveXML(oss2/*, cereal::XMLOutputArchive::Options().indent(true).outputType(false).sizeAttributes(false)*/);archiveXML(::cereal::make_nvp("zoo", list_Animal));
}
std::string xmlStr2 = oss2.str(); // 獲取XML字符串
qDebug() << __LINE__ << QString::fromStdString(xmlStr2);// ===== 2. 從字符串反序列化 =====
QList<Animal> list_Animal_in;
std::istringstream iss2(xmlStr2); // 輸入流綁定XML字符串
{cereal::XMLInputArchive archiveXMLIn(iss2);archiveXMLIn(cereal::make_nvp("zoo", list_Animal_in));
}
qDebug() << __LINE__ << list_Animal_in.size();

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/87253.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/87253.shtml
英文地址,請注明出處:http://en.pswp.cn/web/87253.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Spark 寫入hive表解析

FileOutputCommitter中提交mapreduce.fileoutputcommitter.algorithm.version有v1和v2兩個版本。 v1版本Spark寫入文件的流程&#xff1a; 1.當task完成的時候&#xff0c;會將task的結果文件先寫入到臨時目錄下面。 2.所有的task完成后&#xff0c;將所有的結果文件寫入到結…

Linux云計算基礎篇(5)

一、sudo是什么&#xff1f; 定義&#xff1a;sudo(SuperUserDO)是一個Linux/Unix系統命令&#xff0c;允許被授權的普通用戶以另一個用戶&#xff08;通常是超級用戶root&#xff09;的身份執行命令。 核心目的&#xff1a; 1.最小權限原則&#xff1a;避免讓用戶長期擁有ro…

Postgresql通過pgpool進行高可用部署主從,災備(單機版)

1、bitnami/postgresql-repmgr:15 &#xff08;鏡像名&#xff09; Bitnami 的 PostgreSQL-Repmgr 鏡像是一個預配置的 Docker 鏡像&#xff0c;集成了 PostgreSQL 數據庫和 repmgr&#xff08;Replication Manager&#xff09;工具&#xff0c;用于快速搭建高可用&#xff08…

Flink-1.19.0源碼詳解-番外補充3-StreamGraph圖

1.StreamGraph圖: StreamGraph是Flink流處理作業的第一個計算調度流圖&#xff0c;它是從用戶編寫的 DataStream API程序轉換而來的邏輯圖。StreamGraph由StreamNode與StreamEdge組成&#xff0c;StreamNode為記錄數據處理的節點&#xff0c;StreamEdge為連接兩個StreamNode的邊…

linux系統---Nginx反向代理與緩存功能

目錄 正向代理和反向代理 正向代理的作用 反向代理可實現的功能 反向代理客戶端ip透傳 1.初始訪問192.168.235.139 結果 2.編輯代理服務器的配置文件 3、重載nginx服務 4、訪問代理服務器 實現反向代理負載均衡 1.先啟用已用另一臺服務端 2.使用192.168.235.140 …

U+平臺配置免密登錄、安裝Hadoop配置集群、Spark配置

文章目錄 1、免密登錄2、安裝hadoop3、Spark配置 具體詳細報告見資源部分&#xff0c;全部實驗內容已經上傳&#xff0c;如有需要請自行下載。 1、免密登錄 使用的配置命令&#xff1a; cd ~/.ssh/ssh-keygen -t rsaEnter鍵回車y回車回車出現如上所示 cat ./id_rsa.pub >…

GitHub vs GitLab 全面對比報告(2025版)

從技術架構到金融估值&#xff0c;深度解析兩大代碼托管平臺的差異化競爭策略 一、技術架構對比 維度GitHub (Microsoft旗下)GitLab (獨立上市公司)關鍵差異核心架構- 分布式Git倉庫 Issues/Projects- 全棧DevSecOps平臺GitLab集成CI/CD、安全、監控部署模式- SaaS為主 - Git…

Python 數據分析與可視化 Day 14 - 建模復盤 + 多模型評估對比(邏輯回歸 vs 決策樹)

? 今日目標 回顧整個本周數據分析 & 建模流程學會訓練第二種模型&#xff1a;決策樹&#xff08;Decision Tree&#xff09;掌握多模型對比評估的方法與實踐輸出綜合對比報告&#xff1a;準確率、精確率、召回率、F1 等指標為后續模型調優與擴展打下基礎 &#x1fa9c; 一…

本周大模型新動向:KV緩存混合精度量化、個體時空行為生成、個性化問答

點擊藍字 關注我們 AI TIME歡迎每一位AI愛好者的加入&#xff01; 01 KVmix: Gradient-Based Layer Importance-Aware Mixed-Precision Quantization for KV Cache 大型語言模型&#xff08;LLMs&#xff09;在推理過程中&#xff0c;鍵值&#xff08;KV&#xff09;緩存的高內…

在 Spring Boot 中使用 WebMvcConfigurer

WebMvcConfigurer 是 Spring MVC 提供的一個擴展接口&#xff0c;用于配置 Spring MVC 的各種功能。在 Spring Boot 應用中&#xff0c;通過實現 WebMvcConfigurer 接口&#xff0c;可以定制和擴展默認的 Spring MVC 配置。以下是對 WebMvcConfigurer 的詳細解析及其常見用法。…

w-筆記:uni-app的H5平臺和非H5平臺的拍照識別功能:

uni-app的H5平臺和非H5平臺的拍照識別功能&#xff1a; <template><view class"humanVehicleBinding"><view v-if"warn" class"shadow"></view><view class"header"><uni-nav-bar left-icon"l…

TCP 半連接隊列和全連接隊列(結合 Linux 2.6.32 內核源碼分析)

文章目錄 一、什么是 TCP 半連接隊列和全連接隊列二、TCP 全連接隊列1、如何查看進程的 TCP 全連接隊列大小&#xff1f;注意 2、TCP 全連接隊列溢出問題注意 3、TCP 全連接隊列最大長度 三、TCP 半連接隊列1、TCP 半連接隊列溢出問題2、TCP 半連接隊列最大長度3、引申問題 一、…

linux下fabric環境搭建

參考教程&#xff1a; https://devpress.csdn.net/cloudnative/66d58e702045de334a569db3.html?dp_tokeneyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MjA2MzY4NywiZXhwIjoxNzQwMzY4MDc0LCJpYXQiOjE3Mzk3NjMyNzQsInVzZXJuYW1lIjoiaHVhbmd0dXBpIn0.oh8e4F6Sw_A4SV2ODQ5W0pYK0…

Redis Pipeline介紹:提高操作Redis數據庫的執行效率

Redis Pipeline是一種用于提高Redis執行效率的技術&#xff0c;通過減少客戶端與服務器之間的通信開銷&#xff0c;顯著提升批量操作的性能。本文將詳細介紹Redis Pipeline的概念、使用場景、實現方式及其優勢。 一、Redis Pipeline的概念 Redis Pipeline是一種批處理機制&am…

linux長時間鎖屏無法喚醒

是的&#xff0c;您這么理解很直接&#xff0c;抓住了要點。 簡單來說&#xff0c;就是這樣&#xff1a; 電腦睡覺有兩種方式&#xff1a; 打個盹&#xff08;掛起/Suspend&#xff09;&#xff1a; 把工作狀態保存在內存里。這個一般和 Swap 分區沒關系。睡死過去&#xff…

STM32F103_Bootloader程序開發11 - 實現 App 安全跳轉至 Bootloader

導言 想象一下&#xff0c;我們的單片機 App 正在穩定地運行著&#xff0c;突然我們想給它升級一下&#xff0c;添加個新功能。我們該如何安全地通知它&#xff1a;“嘿&#xff0c;準備好接收新固件了” ? 這就需要 App 和 Bootloader 之間建立一個可靠的"秘密握手"…

Explain解釋

參考官方文檔&#xff1a;https://dev.mysql.com/doc/refman/5.7/en/explain-output.html explain關鍵字可以分析你的查詢語句的結構和性能。 explain select查詢&#xff0c; 執行會返回執行計劃的信息。 注意&#xff1a;如果from中有子查詢&#xff0c;仍然會執行該子查詢…

選擇 PDF 轉 HTML 轉換器的 5 個關鍵特性

市面上有很多 PDF 轉 HTML 的轉換器&#xff0c;每一款產品都有不同的功能組合。要理清并理解每個功能可能會讓人感到困惑。那么&#xff0c;真正重要的是什么呢&#xff1f; 這篇文章將介紹我們認為在選擇最佳 PDF 轉 HTML 轉換器時最重要的 5 個關鍵特性&#xff1a; 1. 轉換…

使用堡塔在服務器上部署寶塔面板-linux版

使用堡塔在服務器上部署寶塔面板-linux版 使用堡塔多機管理登錄服務器 進入寶塔官網&#xff0c;獲取安裝腳本 wget -O install_panel.sh https://download.bt.cn/install/install_panel.sh && sudo bash install_panel.sh ed8484bec3. 在堡塔多機管理中&#xff0c;…

【Unity高級】Unity多界面游戲場景管理方案詳解

引言&#xff1a;游戲界面管理的挑戰 在Unity游戲開發中&#xff0c;尤其是包含多個功能界面&#xff08;如主菜單、關卡選擇、游戲頁面、設置和商城&#xff09;的游戲&#xff0c;如何高效管理場景與界面是架構設計的核心挑戰。本文將深入探討三種主流實現方案&#xff1a;單…