Gunakan antarmuka IXMLSerializable untuk mengisi kelas dari file XML
Abstrak
Tahukah Anda bahwa Kelas Umum PLCnext telah mendukung serialisasi XML? Artikel ini menunjukkan cara menggunakan IXmlSerializable
antarmuka untuk mengisi data dalam kelas c++.
Anda dapat menemukan deskripsi Antarmuka dalam dokumentasi API Kelas Umum PLCnext.
Persyaratan
Artikel ini ditulis dengan pengaturan berikut:
PLCnext Firmware: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>
$ARP_DATA_DIR$
notasi 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 IXMLSerializable
antarmuka untuk kelas kami. Untuk membuatnya tetap sederhana, kelas kami hanya memiliki dua elemen data, 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 menerapkan ReadXml
dan WriteXml
Metode.
WriteXml
Metodenya lurus ke depan, kami tidak ingin menulis kami hanya ingin membaca data dari file XML. ReadXml
Metode 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
Baca data
Kita sekarang dapat menggunakan kelas kita dengan XMLConfigDocument
class di Metode LoadConfig untuk memuat data di 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());
}
}