History

TimeSeriesPlatformLibrary: Added XML generation functions for DataSet.

J. Ritchie Carroll committed 7 years ago

Commit 0b134b41 parent 285644b14 files · +4485 −628Browse files

Showing the changes to Source/Libraries/TimeSeriesPlatformLibrary/Data/DataSet.cpp (1 of 14 files). Show all

Unified · Split

Source/Libraries/TimeSeriesPlatformLibrary/Data/DataSet.cpp+371−67
@@ -29,6 +29,26 @@ using namespace pugi; · 28 unmodified lines
2929 using namespace GSF;
3030 using namespace GSF::Data;
3131
32+class XMLVectorWriter: public xml_writer
33+{
34+private:
35+ vector<uint8_t>& m_target;
36+
37+public:
38+ XMLVectorWriter(vector<uint8_t>& target) :
39+ m_target(target)
40+ {
41+ }
42+
43+ void write(const void* data, size_t size) override
44+ {
45+ const char* chunk = static_cast<const char*>(data);
46+
47+ for (size_t i = 0; i < size; i++)
48+ m_target.push_back(static_cast<uint8_t>(chunk[i]));
49+ }
50+};
51+
3252 DataSetException::DataSetException(string message) noexcept :
3353 m_message(move(message))
3454 {
@@ -72,10 +92,24 @@ int32_t DataSet::TableCount() const · 37 unmodified lines
7292 return m_tables.size();
7393 }
7494
75−void DataSet::IterateTables(TableIteratorHandlerFunction iteratorHandler, void* userData)
95+vector<string> DataSet::TableNames() const
7696 {
97+ vector<string> tableNames;
98+
7799 for (auto const& item : m_tables)
78− iteratorHandler(item.second, userData);
100+ tableNames.push_back(item.first);
101+
102+ return tableNames;
103+}
104+
105+vector<DataTablePtr> DataSet::Tables() const
106+{
107+ vector<DataTablePtr> tables;
108+
109+ for (auto const& item : m_tables)
110+ tables.push_back(item.second);
111+
112+ return tables;
79113 }
80114
81115 bool DataSet::AddOrUpdateTable(DataTablePtr table)
@@ -89,18 +123,61 @@ bool DataSet::RemoveTable(const string& tableName) · 7 unmodified lines
89123 return m_tables.erase(tableName) > 0;
90124 }
91125
92−DataSetPtr DataSet::ParseXmlDataSet(const vector<uint8_t>& xmlDataSet)
126+void DataSet::ReadXml(const string& fileName)
93127 {
94128 xml_document document;
95129
96− // Load dataset into an XML parser
97− const xml_parse_result result = document.load_buffer_inplace(const_cast<uint8_t*>(xmlDataSet.data()), xmlDataSet.size());
130+ const xml_parse_result result = document.load_file(fileName.c_str());
98131
99132 if (result.status != xml_parse_status::status_ok)
100− throw DataSetException("Failed to parse dataset XML: " + string(result.description()));
133+ throw DataSetException("Failed to load XML from file: " + string(result.description()));
134+
135+ ParseXml(document);
136+}
101137
138+void DataSet::ReadXml(const vector<uint8_t>& buffer)
139+{
140+ xml_document document;
141+
142+ const xml_parse_result result = document.load_buffer_inplace(const_cast<uint8_t*>(buffer.data()), buffer.size());
143+
144+ if (result.status != xml_parse_status::status_ok)
145+ throw DataSetException("Failed to load XML from buffer: " + string(result.description()));
146+
147+ ParseXml(document);
148+}
149+
150+void DataSet::WriteXml(const string& fileName, const string& dataSetName) const
151+{
152+ xml_document document;
153+ GenerateXml(document, dataSetName);
154+ document.save_file(fileName.c_str(), " ", format_default | format_save_file_text);
155+}
156+
157+void DataSet::WriteXml(vector<uint8_t>& buffer, const string& dataSetName) const
158+{
159+ xml_document document;
160+ XMLVectorWriter writer(buffer);
161+ GenerateXml(document, dataSetName);
162+ document.save(writer, " ");
163+}
164+
165+DataSetPtr DataSet::FromXml(const std::string& fileName)
166+{
102167 DataSetPtr dataSet = NewSharedPtr<DataSet>();
168+ dataSet->ReadXml(fileName);
169+ return dataSet;
170+}
103171
172+DataSetPtr DataSet::FromXml(const std::vector<uint8_t>& buffer)
173+{
174+ DataSetPtr dataSet = NewSharedPtr<DataSet>();
175+ dataSet->ReadXml(buffer);
176+ return dataSet;
177+}
178+
179+void DataSet::ParseXml(const xml_document& document)
180+{
104181 // Find root node
105182 xml_node rootNode = document.document_element();
106183 const string rootNodeName = string(rootNode.name());
@@ -158,7 +235,7 @@ DataSetPtr DataSet::ParseXmlDataSet(const vector<uint8_t>& xmlDataSet) · 51 unmodified lines
158235
159236 xml_attribute nameAttribute, typeAttribute, extDataTypeAttribute, extExpressionAttribute;
160237
161− // Find element node
238+ // Find schema element node
162239 xml_node elementNode;
163240 const string elementNodeName = schemaPrefix + "element";
164241
@@ -241,7 +318,7 @@ DataSetPtr DataSet::ParseXmlDataSet(const vector<uint8_t>& xmlDataSet) · 76 unmodified lines
241318 if (!IsEqual(sequenceNode.name(), schemaPrefix + "sequence", false))
242319 continue;
243320
244− const DataTablePtr dataTable = NewSharedPtr<DataTable>(dataSet, tableName);
321+ const DataTablePtr dataTable = CreateTable(tableName);
245322
246323 // Each sequence node child element node represents a table field definition
247324 for (xml_node node = sequenceNode.first_child(); node; node = node.next_sibling())
@@ -295,66 +372,66 @@ DataSetPtr DataSet::ParseXmlDataSet(const vector<uint8_t>& xmlDataSet) · 47 unmodified lines
295372 columnExpression = extExpressionAttribute.value();
296373 }
297374
298− DataType columnDataType;
375+ DataType dataType;
299376
300377 if (IsEqual(typeName, "string", false))
301378 {
302379 if (!extDataType.empty() && StartsWith(extDataType, "System.Guid", false))
303− columnDataType = DataType::Guid;
380+ dataType = DataType::Guid;
304381 else
305− columnDataType = DataType::String;
382+ dataType = DataType::String;
306383 }
307384 else if (IsEqual(typeName, "boolean", false))
308385 {
309− columnDataType = DataType::Boolean;
386+ dataType = DataType::Boolean;
310387 }
311388 else if (IsEqual(typeName, "dateTime", false))
312389 {
313− columnDataType = DataType::DateTime;
390+ dataType = DataType::DateTime;
314391 }
315392 else if (IsEqual(typeName, "float", false))
316393 {
317− columnDataType = DataType::Single;
394+ dataType = DataType::Single;
318395 }
319396 else if (IsEqual(typeName, "double", false))
320397 {
321− columnDataType = DataType::Double;
398+ dataType = DataType::Double;
322399 }
323400 else if (IsEqual(typeName, "decimal", false))
324401 {
325− columnDataType = DataType::Decimal;
402+ dataType = DataType::Decimal;
326403 }
327404 else if (IsEqual(typeName, "byte", false))
328405 {
329− columnDataType = DataType::Int8;
406+ dataType = DataType::Int8;
330407 }
331408 else if (IsEqual(typeName, "short", false))
332409 {
333− columnDataType = DataType::Int16;
410+ dataType = DataType::Int16;
334411 }
335412 else if (IsEqual(typeName, "int", false))
336413 {
337− columnDataType = DataType::Int32;
414+ dataType = DataType::Int32;
338415 }
339416 else if (IsEqual(typeName, "long", false))
340417 {
341− columnDataType = DataType::Int64;
418+ dataType = DataType::Int64;
342419 }
343420 else if (IsEqual(typeName, "unsignedByte", false))
344421 {
345− columnDataType = DataType::UInt8;
422+ dataType = DataType::UInt8;
346423 }
347424 else if (IsEqual(typeName, "unsignedShort", false))
348425 {
349− columnDataType = DataType::UInt16;
426+ dataType = DataType::UInt16;
350427 }
351428 else if (IsEqual(typeName, "unsignedInt", false))
352429 {
353− columnDataType = DataType::UInt32;
430+ dataType = DataType::UInt32;
354431 }
355432 else if (IsEqual(typeName, "unsignedLong", false))
356433 {
357− columnDataType = DataType::UInt64;
434+ dataType = DataType::UInt64;
358435 }
359436 else
360437 {
@@ -364,116 +441,343 @@ DataSetPtr DataSet::ParseXmlDataSet(const vector<uint8_t>& xmlDataSet) · 3 unmodified lines
364441 }
365442
366443 // Create column
367− const DataColumnPtr dataColumn = dataTable->CreateColumn(columnName, columnDataType, columnExpression);
444+ const DataColumnPtr dataColumn = dataTable->CreateColumn(columnName, dataType, columnExpression);
368445 dataTable->AddColumn(dataColumn);
369446 }
370447
371− dataSet->AddOrUpdateTable(dataTable);
448+ AddOrUpdateTable(dataTable);
372449 }
373450
374451 // Each root node child that matches a table name represents a record
375452 for (xml_node recordNode = rootNode.first_child(); recordNode; recordNode = recordNode.next_sibling())
376453 {
377− const DataTablePtr dataTable = dataSet->Table(recordNode.name());
454+ const DataTablePtr table = Table(recordNode.name());
378455
379− if (dataTable == nullptr)
456+ if (table == nullptr)
380457 continue;
381458
382− const DataRowPtr dataRow = dataTable->CreateRow();
459+ const DataRowPtr row = table->CreateRow();
383460
384461 // Each record node child represents a field value
385462 for (xml_node fieldNode = recordNode.first_child(); fieldNode; fieldNode = fieldNode.next_sibling())
386463 {
387− const DataColumnPtr dataColumn = dataTable->Column(fieldNode.name());
464+ const DataColumnPtr column = table->Column(fieldNode.name());
388465
389− if (dataColumn == nullptr)
466+ if (column == nullptr)
390467 continue;
391468
392− const int32_t index = dataColumn->Index();
393− const xml_text value = fieldNode.text();
469+ const int32_t columnIndex = column->Index();
470+ const xml_text nodeText = fieldNode.text();
394471
395− switch (dataColumn->Type())
472+ switch (column->Type())
396473 {
397474 case DataType::String:
398− dataRow->SetStringValue(index, string(value.as_string("")));
475+ row->SetStringValue(columnIndex, string(nodeText.as_string("")));
399476 break;
400477 case DataType::Boolean:
401− dataRow->SetBooleanValue(index, value.as_bool());
478+ row->SetBooleanValue(columnIndex, nodeText.as_bool());
402479 break;
403480 case DataType::DateTime:
404− if (value.empty())
405− dataRow->SetDateTimeValue(index, Empty::DateTime);
481+ if (nodeText.empty())
482+ row->SetDateTimeValue(columnIndex, Empty::DateTime);
406483 else
407− dataRow->SetDateTimeValue(index, ParseTimestamp(value.as_string()));
484+ row->SetDateTimeValue(columnIndex, ParseTimestamp(nodeText.as_string()));
408485 break;
409486 case DataType::Single:
410− dataRow->SetSingleValue(index, value.as_float());
487+ row->SetSingleValue(columnIndex, nodeText.as_float());
411488 break;
412489 case DataType::Double:
413− dataRow->SetDoubleValue(index, value.as_double());
490+ row->SetDoubleValue(columnIndex, nodeText.as_double());
414491 break;
415492 case DataType::Decimal:
416− if (value.empty())
417− dataRow->SetDecimalValue(index, decimal_t(0));
493+ if (nodeText.empty())
494+ row->SetDecimalValue(columnIndex, decimal_t(0));
418495 else
419− dataRow->SetDecimalValue(index, decimal_t(value.as_string()));
496+ row->SetDecimalValue(columnIndex, decimal_t(nodeText.as_string()));
420497 break;
421498 case DataType::Guid:
422− if (value.empty())
423− dataRow->SetGuidValue(index, Empty::Guid);
499+ if (nodeText.empty())
500+ row->SetGuidValue(columnIndex, Empty::Guid);
424501 else
425− dataRow->SetGuidValue(index, ParseGuid(value.as_string()));
502+ row->SetGuidValue(columnIndex, ParseGuid(nodeText.as_string()));
426503 break;
427504 case DataType::Int8:
428− dataRow->SetInt8Value(index, static_cast<int8_t>(value.as_int()));
505+ row->SetInt8Value(columnIndex, static_cast<int8_t>(nodeText.as_int()));
429506 break;
430507 case DataType::Int16:
431− dataRow->SetInt16Value(index, static_cast<int16_t>(value.as_int()));
508+ row->SetInt16Value(columnIndex, static_cast<int16_t>(nodeText.as_int()));
432509 break;
433510 case DataType::Int32:
434− dataRow->SetInt32Value(index, value.as_int());
511+ row->SetInt32Value(columnIndex, nodeText.as_int());
435512 break;
436513 case DataType::Int64:
437− dataRow->SetInt64Value(index, value.as_llong());
514+ row->SetInt64Value(columnIndex, nodeText.as_llong());
438515 break;
439516 case DataType::UInt8:
440− dataRow->SetUInt8Value(index, static_cast<uint8_t>(value.as_uint()));
517+ row->SetUInt8Value(columnIndex, static_cast<uint8_t>(nodeText.as_uint()));
441518 break;
442519 case DataType::UInt16:
443− dataRow->SetUInt16Value(index, static_cast<uint16_t>(value.as_uint()));
520+ row->SetUInt16Value(columnIndex, static_cast<uint16_t>(nodeText.as_uint()));
444521 break;
445522 case DataType::UInt32:
446− dataRow->SetUInt32Value(index, value.as_uint());
523+ row->SetUInt32Value(columnIndex, nodeText.as_uint());
447524 break;
448525 case DataType::UInt64:
449− dataRow->SetUInt64Value(index, value.as_ullong());
526+ row->SetUInt64Value(columnIndex, nodeText.as_ullong());
450527 break;
451528 default:
452529 throw DataSetException("Unexpected column data type encountered");
453530 }
454531 }
455532
456− dataTable->AddRow(dataRow);
533+ table->AddRow(row);
457534 }
458−
459− return dataSet;
460535 }
461536
462−vector<uint8_t> DataSet::GenerateXmlDataSet(const DataSetPtr& dataSet, const string& dataSetName)
537+void DataSet::GenerateXml(xml_document& document, const string& dataSetName) const
463538 {
464− xml_document document;
465539 const char* rootNodeName = dataSetName.c_str();
466−
540+ const char* schemaNodeName = "xs:schema";
541+ const char* elementNodeName = "xs:element";
542+ const char* complexNodeName = "xs:complexType";
543+ const char* choiceNodeName = "xs:choice";
544+ const char* sequenceNodeName = "xs:sequence";
545+ const char* extDataTypeAttributeName = "ext:DataType";
546+ const char* extExpressionAttributeName = "ext:Expression";
547+ const vector<DataTablePtr> tables = Tables();
548+
549+ // <?xml version="1.0" standalone="yes"?>
550+ xml_node declaration = document.prepend_child(node_declaration);
551+ declaration.append_attribute("version") = "1.0";
552+ declaration.append_attribute("standalone") = "yes";
553+
554+ // <DataSet>
467555 xml_node rootNode = document.append_child(rootNodeName);
468556
469− // <xs:schema id="DataSet" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:ext="urn:schemas-microsoft-com:xml-msdata">
470− xml_node schemaNode = rootNode.append_child("xs:schema");
471−
557+ // <xs:schema id="DataSet" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:ext="urn:schemas-microsoft-com:xml-msdata">
558+ xml_node schemaNode = rootNode.append_child(schemaNodeName);
472559 schemaNode.append_attribute("id") = rootNodeName;
473560 schemaNode.append_attribute("xmlns:xs") = XmlSchemaNamespace.c_str();
474561 schemaNode.append_attribute("xmlns:ext") = ExtXmlSchemaDataNamespace.c_str();
475562
476− vector<uint8_t> xmlDataSet;
563+ // <xs:element name="DataSet">
564+ xml_node elementNode = schemaNode.append_child(elementNodeName);
565+ elementNode.append_attribute("name") = rootNodeName;
477566
478− return xmlDataSet;
479−}
567+ // <xs:complexType>
568+ xml_node complexNode = elementNode.append_child(complexNodeName);
569+
570+ // <xs:choice minOccurs="0" maxOccurs="unbounded">
571+ xml_node choiceNode = complexNode.append_child(choiceNodeName);
572+ choiceNode.append_attribute("minOccurs") = "0";
573+ choiceNode.append_attribute("maxOccurs") = "unbounded";
574+
575+ // Write schema definition for each table
576+ for (auto const& table : tables)
577+ {
578+ // <xs:element name="TableName">
579+ elementNode = choiceNode.append_child(elementNodeName);
580+ elementNode.append_attribute("name") = table->Name().c_str();
581+
582+ // <xs:complexType>
583+ complexNode = elementNode.append_child(complexNodeName);
584+
585+ // <xs:sequence>
586+ xml_node sequenceNode = complexNode.append_child(sequenceNodeName);
587+
588+ // Write schema definition for each table field
589+ for (int32_t columnIndex = 0; columnIndex < table->ColumnCount(); columnIndex++)
590+ {
591+ const DataColumnPtr& column = table->Column(columnIndex);
592+
593+ // <xs:element name="FieldName" type="xs:string" minOccurs="0" />
594+ elementNode = sequenceNode.append_child(elementNodeName);
595+ elementNode.append_attribute("name") = column->Name().c_str();
596+
597+ // Map DataType to XML schema type
598+ const char* dataType = [column]()
599+ {
600+ switch (column->Type())
601+ {
602+ case DataType::String:
603+ case DataType::Guid:
604+ return "xs:string";
605+ case DataType::Boolean:
606+ return "xs:boolean";
607+ case DataType::DateTime:
608+ return "xs:dateTime";
609+ case DataType::Single:
610+ return "xs:float";
611+ case DataType::Double:
612+ return "xs:double";
613+ case DataType::Decimal:
614+ return "xs:decimal";
615+ case DataType::Int8:
616+ return "xs:byte";
617+ case DataType::Int16:
618+ return "xs:short";
619+ case DataType::Int32:
620+ return "xs:int";
621+ case DataType::Int64:
622+ return "xs:long";
623+ case DataType::UInt8:
624+ return "xs:unsignedByte";
625+ case DataType::UInt16:
626+ return "xs:unsignedShort";
627+ case DataType::UInt32:
628+ return "xs:unsignedInt";
629+ case DataType::UInt64:
630+ return "xs:unsignedLong";
631+ default:
632+ throw DataSetException("Unexpected column data type encountered");
633+ }
634+ }();
635+
636+ // Guid is an extended schema data type: ext:DataType="System.Guid"
637+ if (column->Type() == DataType::Guid)
638+ elementNode.append_attribute(extDataTypeAttributeName) = "System.Guid";
639+
640+ // Computed columns define an expression: ext:Expression="FieldNameA + FieldNameB"
641+ if (column->Computed())
642+ elementNode.append_attribute(extExpressionAttributeName) = column->Expression().c_str();
643+
644+ elementNode.append_attribute("type") = dataType;
645+ elementNode.append_attribute("minOccurs") = "0";
646+ }
647+ }
648+
649+ // Write records for each table
650+ for (auto const& table : tables)
651+ {
652+ const char* tableName = table->Name().c_str();
653+
654+ for (int32_t rowIndex = 0; rowIndex < table->RowCount(); rowIndex++)
655+ {
656+ const DataRowPtr& row = table->Row(rowIndex);
657+
658+ if (row == nullptr)
659+ continue;
660+
661+ xml_node recordNode = rootNode.append_child(tableName);
662+
663+ for (int32_t columnIndex = 0; columnIndex < table->ColumnCount(); columnIndex++)
664+ {
665+ // Null records are not written into the XML document
666+ if (row->IsNull(columnIndex))
667+ continue;
668+
669+ const DataColumnPtr& column = table->Column(columnIndex);
670+
671+ // Computed records are not written into the XML document
672+ if (column->Computed())
673+ continue;
674+
675+ xml_node fieldNode = recordNode.append_child(column->Name().c_str());
676+ xml_text nodeText = fieldNode.text();
677+
678+ switch (column->Type())
679+ {
680+ case DataType::String:
681+ {
682+ auto result = row->ValueAsString(columnIndex);
683+ nodeText.set(result.GetValueOrDefault().c_str());
684+ break;
685+ }
686+ case DataType::Boolean:
687+ {
688+ auto result = row->ValueAsBoolean(columnIndex);
689+ nodeText.set(result.GetValueOrDefault() ? "true" : "false");
690+ break;
691+ }
692+ case DataType::DateTime:
693+ {
694+ auto result = row->ValueAsDateTime(columnIndex);
695+ string dateTime = ToString(result.GetValueOrDefault(), "%Y-%m-%dT%H:%M:%S%F");
696+
697+ if (Contains(dateTime, ".", false))
698+ dateTime = TrimRight(dateTime, "0");
699+
700+ dateTime.append("Z");
701+
702+ nodeText.set(dateTime.c_str());
703+ break;
704+ }
705+ case DataType::Single:
706+ {
707+ auto result = row->ValueAsSingle(columnIndex);
708+ nodeText.set(result.GetValueOrDefault());
709+ break;
710+ }
711+ case DataType::Double:
712+ {
713+ auto result = row->ValueAsDouble(columnIndex);
714+ nodeText.set(result.GetValueOrDefault());
715+ break;
716+ }
717+ case DataType::Decimal:
718+ {
719+ auto result = row->ValueAsDecimal(columnIndex);
720+ nodeText.set(result.GetValueOrDefault().str().c_str());
721+ break;
722+ }
723+ case DataType::Guid:
724+ {
725+ auto result = row->ValueAsGuid(columnIndex);
726+ nodeText.set(ToString(result.GetValueOrDefault()).c_str());
727+ break;
728+ }
729+ case DataType::Int8:
730+ {
731+ auto result = row->ValueAsInt8(columnIndex);
732+ nodeText.set(static_cast<int>(result.GetValueOrDefault()));
733+ break;
734+ }
735+ case DataType::Int16:
736+ {
737+ auto result = row->ValueAsInt16(columnIndex);
738+ nodeText.set(static_cast<int>(result.GetValueOrDefault()));
739+ break;
740+ }
741+ case DataType::Int32:
742+ {
743+ auto result = row->ValueAsInt32(columnIndex);
744+ nodeText.set(result.GetValueOrDefault());
745+ break;
746+ }
747+ case DataType::Int64:
748+ {
749+ auto result = row->ValueAsInt64(columnIndex);
750+ nodeText.set(result.GetValueOrDefault());
751+ break;
752+ }
753+ case DataType::UInt8:
754+ {
755+ auto result = row->ValueAsUInt8(columnIndex);
756+ nodeText.set(static_cast<unsigned>(result.GetValueOrDefault()));
757+ break;
758+ }
759+ case DataType::UInt16:
760+ {
761+ auto result = row->ValueAsUInt16(columnIndex);
762+ nodeText.set(static_cast<unsigned>(result.GetValueOrDefault()));
763+ break;
764+ }
765+ case DataType::UInt32:
766+ {
767+ auto result = row->ValueAsUInt32(columnIndex);
768+ nodeText.set(result.GetValueOrDefault());
769+ break;
770+ }
771+ case DataType::UInt64:
772+ {
773+ auto result = row->ValueAsUInt64(columnIndex);
774+ nodeText.set(result.GetValueOrDefault());
775+ break;
776+ }
777+ default:
778+ throw DataSetException("Unexpected column data type encountered");
779+ }
780+ }
781+ }
782+ }
783+}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t