在 PLCnext 中使用 IXMLSerializable 从 XML 填充 C++ 类
摘要
您知道 PLCnext Common Classes 内置支持 XML 序列化吗?本文展示如何使用 IXmlSerializable 接口填充 C++ 类数据。
接口描述见 PLCnext Common Classes 的 API 文档。
要求
本文基于以下环境编写:
PLCnext Firmware: 2020.6 LTS PLCnext C++ SDK for Linux 64 Bit 2020.6 LTS
数据
我们要用以下配置文件填充类。
<?xml version="1.0" encoding="UTF-8"?> <MyConfigDocument schemaVersion="1.0"> <Server dnsName="server.domain.tld" /> <FileList> <File path="$ARP_DATA_DIR$/Services/MyComponent/file1.txt" /> <File path="$ARP_DATA_DIR$/Services/MyComponent/file2.txt" /> </FileList> </MyConfigDocument>
$ARP_DATA_DIR$ 为环境变量占位符,此处为 ARP_DATA_DIR。可在目标设备 /etc/plcnext/Device.acf.settings 设置文件中找到定义的 Arp 环境变量。
要从 XML 文件读取数据,必须为类实现 IXMLSerializable 接口。为简单起见,类仅有两个数据元素:DNS 名称和文件路径向量。
#pragma once #include "Arp/System/Core/Arp.h" #include "Arp/System/Commons/Xml/IXmlSerializable.hpp" #include "vector" namespace MyComponent { class MyConfiguration : public Arp::System::Commons::Xml::IXmlSerializable { public: MyConfiguration() = default; ~MyConfiguration() = default; // IXMLSerializable interface public: void ReadXml(Arp::System::Commons::Xml::XmlReader& reader, Arp::System::Commons::Xml::XmlSerializationContext& context) override; void WriteXml(Arp::System::Commons::Xml::XmlWriter& writer, Arp::System::Commons::Xml::XmlSerializationContext& context) override; // The data public: Arp::String DnsName{""}; std::vector<Arp::String> FileList; // Some supporting methods private: void readFileList(Arp::System::Commons::Xml::XmlReader& reader, Arp::System::Commons::Xml::XmlSerializationContext& context); void readFile(Arp::System::Commons::Xml::XmlReader& reader, Arp::System::Commons::Xml::XmlSerializationContext& context); }; } // namespace MyComponent
实现
必须实现 ReadXml 和 WriteXml 方法。
WriteXml 方法直截了当,我们不写入只读取 XML 文件数据。ReadXml 方法在读取 XML 文件数据时调用。
#include "MyConfiguration.hpp" namespace MyComponent { void MyConfiguration::WriteXml(Arp::System::Commons::Xml::XmlWriter& writer, Arp::System::Commons::Xml::XmlSerializationContext& context) { // no operation. return; } void MyConfiguration::ReadXml(Arp::System::Commons::Xml::XmlReader& reader, Arp::System::Commons::Xml::XmlSerializationContext& context) { Arp::String elementName; while (reader.TryReadStartElement(elementName)) { if (elementName == Arp::System::Commons::Xml::XmlSerializationContext::IncludesXmlName) { context.ReadIncludesElement(reader); } else if (elementName == "Server") { this->DnsName = reader.GetAttributeValue<Arp::String>("dnsName"); reader.ReadEndElement(); } else if (elementName == "FileList") { this->readFileList(reader, context); } else { context.InvalidXmlElementOccurs(reader, elementName); reader.ReadEndElement(); } } } void MyConfiguration::readFileList(Arp::System::Commons::Xml::XmlReader& reader, Arp::System::Commons::Xml::XmlSerializationContext& context) { if (reader.IsEmptyElement()){ return; } if (reader.ReadToDescendant("File")) { this->readFile(reader, context); while (reader.ReadToNextSibling("File")) { this->readFile(reader, context); } } else { reader.ReadEndElement(); } } void MyConfiguration::readFile(Arp::System::Commons::Xml::XmlReader& reader, Arp::System::Commons::Xml::XmlSerializationContext& context) { // Use 'context.ResolvePath' to replace placeholders in the path. auto file = Arp::String(context.ResolvePath(reader.GetAttributeValue<Arp::String>("path"))); this->FileList.push_back(file); reader.ReadEndElement(); } } // namespace MyComponent
读取数据
现可在 LoadConfig 方法中用 XMLConfigDocument 类加载数据到类中。
void MyComponent::LoadConfig() { // load project config here using namespace Arp::System::Commons; this->log.Info("LoadConfig"); // Fist argument has to match the XML root element name. // Our MyConfiguration instance this->config will be populated. Xml::XmlConfigDocument configDoc("MyConfigDocument", this->config); if (!Io::File::Exists(this->settingsPath)) { this->log.Error("Configuration file '{}' does not exist.", this->settingsPath); return; } try { configDoc.Load(this->settingsPath); } catch (const Arp::Exception& e) { this->log.Error(e.GetMessage()); throw InvalidConfigException(e.GetMessage()); } }
工业技术
- SPICE 命令行界面使用详解
- 数字孪生如何革新制造业:收益、应用与ROI
- 技术如何变革制造业:从3D打印到数字孪生
- 防爆电机选型全攻略:分级分组、认证合规与自燃温度匹配五步法
- 制造业模具类型详解:冲压、拉拔、铸造模具全解析
- 测试点如何提升PCB测试与生产效率
- 自动化如何重塑制造业:提效降本的关键路径
- 工业 4.0 中增强现实 (AR) 的优势
- 掌握 Java BufferedReader:高效字符流处理实战指南
- 掌握 Java 文件操作:java.io 包下的创建、读取、写入与删除
- Java 接口 vs 抽象类:如何选择合适的抽象方式
- Java 接口全解析:定义契约、默认方法、多实现与标记接口