Isi Kelas C++ dari XML Menggunakan IXMLSerializable di PLCnext
Abstrak
Apakah Anda tahu bahwa Common Classes PLCnext memiliki dukungan bawaan untuk serialisasi XML? Artikel ini menunjukkan cara menggunakan antarmuka IXmlSerializable untuk mengisi data dalam sebuah kelas C++.
Anda dapat menemukan deskripsi antarmuka di dokumentasi API dari Common Classes PLCnext.
Persyaratan
Artikel ini ditulis dengan pengaturan berikut:
Firmware PLCnext: 2020.6 LTS PLCnext C++ SDK untuk Linux 64 Bit 2020.6 LTS
Data
Kami ingin mengisi kelas kami dengan file konfigurasi berikut.
<?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>
Notasi $ARP_DATA_DIR$ adalah placeholder untuk variabel lingkungan, dalam hal ini ARP_DATA_DIR. Anda dapat menemukan variabel lingkungan Arp yang ditentukan dalam file pengaturan perangkat Anda pada target /etc/plcnext/Device.acf.settings.
Untuk dapat membaca data dari file XML kita harus mengimplementasikan antarmuka IXMLSerializable untuk kelas kita. Untuk menjaga kesederhanaan kelas kami hanya memiliki dua elemen data, sebuah nama DNS dan vektor jalur file.
#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
Implementasi
Kita harus mengimplementasikan metode ReadXml dan WriteXml.
Metode WriteXml sederhana, kita tidak ingin menulis kita hanya ingin membaca data dari file XML. Metode ReadXml dipanggil jika kita ingin membaca data dari file 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
Membaca Data
Kita sekarang dapat menggunakan kelas kita dengan kelas XMLConfigDocument di dalam metode LoadConfig untuk memuat data ke dalam kelas kita.
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()); } }