Showing the changes to Source/Libraries/TimeSeriesPlatformLibrary/Data/DataSet.cpp (1 of 14 files). Show all
Unified · Split
| @@ -29,6 +29,26 @@ using namespace pugi; · 28 unmodified lines | ||
| 29 | 29 | using namespace GSF; |
| 30 | 30 | using namespace GSF::Data; |
| 31 | 31 | |
| 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 | + | |
| 32 | 52 | DataSetException::DataSetException(string message) noexcept : |
| 33 | 53 | m_message(move(message)) |
| 34 | 54 | { |
| @@ -72,10 +92,24 @@ int32_t DataSet::TableCount() const · 37 unmodified lines | ||
| 72 | 92 | return m_tables.size(); |
| 73 | 93 | } |
| 74 | 94 | |
| 75 | −void DataSet::IterateTables(TableIteratorHandlerFunction iteratorHandler, void* userData) | |
| 95 | +vector<string> DataSet::TableNames() const | |
| 76 | 96 | { |
| 97 | + vector<string> tableNames; | |
| 98 | + | |
| 77 | 99 | 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; | |
| 79 | 113 | } |
| 80 | 114 | |
| 81 | 115 | bool DataSet::AddOrUpdateTable(DataTablePtr table) |
| @@ -89,18 +123,61 @@ bool DataSet::RemoveTable(const string& tableName) · 7 unmodified lines | ||
| 89 | 123 | return m_tables.erase(tableName) > 0; |
| 90 | 124 | } |
| 91 | 125 | |
| 92 | −DataSetPtr DataSet::ParseXmlDataSet(const vector<uint8_t>& xmlDataSet) | |
| 126 | +void DataSet::ReadXml(const string& fileName) | |
| 93 | 127 | { |
| 94 | 128 | xml_document document; |
| 95 | 129 | |
| 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()); | |
| 98 | 131 | |
| 99 | 132 | 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 | +} | |
| 101 | 137 | |
| 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 | +{ | |
| 102 | 167 | DataSetPtr dataSet = NewSharedPtr<DataSet>(); |
| 168 | + dataSet->ReadXml(fileName); | |
| 169 | + return dataSet; | |
| 170 | +} | |
| 103 | 171 | |
| 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 | +{ | |
| 104 | 181 | // Find root node |
| 105 | 182 | xml_node rootNode = document.document_element(); |
| 106 | 183 | const string rootNodeName = string(rootNode.name()); |
| @@ -158,7 +235,7 @@ DataSetPtr DataSet::ParseXmlDataSet(const vector<uint8_t>& xmlDataSet) · 51 unmodified lines | ||
| 158 | 235 | |
| 159 | 236 | xml_attribute nameAttribute, typeAttribute, extDataTypeAttribute, extExpressionAttribute; |
| 160 | 237 | |
| 161 | − // Find element node | |
| 238 | + // Find schema element node | |
| 162 | 239 | xml_node elementNode; |
| 163 | 240 | const string elementNodeName = schemaPrefix + "element"; |
| 164 | 241 | |
| @@ -241,7 +318,7 @@ DataSetPtr DataSet::ParseXmlDataSet(const vector<uint8_t>& xmlDataSet) · 76 unmodified lines | ||
| 241 | 318 | if (!IsEqual(sequenceNode.name(), schemaPrefix + "sequence", false)) |
| 242 | 319 | continue; |
| 243 | 320 | |
| 244 | − const DataTablePtr dataTable = NewSharedPtr<DataTable>(dataSet, tableName); | |
| 321 | + const DataTablePtr dataTable = CreateTable(tableName); | |
| 245 | 322 | |
| 246 | 323 | // Each sequence node child element node represents a table field definition |
| 247 | 324 | 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 | ||
| 295 | 372 | columnExpression = extExpressionAttribute.value(); |
| 296 | 373 | } |
| 297 | 374 | |
| 298 | − DataType columnDataType; | |
| 375 | + DataType dataType; | |
| 299 | 376 | |
| 300 | 377 | if (IsEqual(typeName, "string", false)) |
| 301 | 378 | { |
| 302 | 379 | if (!extDataType.empty() && StartsWith(extDataType, "System.Guid", false)) |
| 303 | − columnDataType = DataType::Guid; | |
| 380 | + dataType = DataType::Guid; | |
| 304 | 381 | else |
| 305 | − columnDataType = DataType::String; | |
| 382 | + dataType = DataType::String; | |
| 306 | 383 | } |
| 307 | 384 | else if (IsEqual(typeName, "boolean", false)) |
| 308 | 385 | { |
| 309 | − columnDataType = DataType::Boolean; | |
| 386 | + dataType = DataType::Boolean; | |
| 310 | 387 | } |
| 311 | 388 | else if (IsEqual(typeName, "dateTime", false)) |
| 312 | 389 | { |
| 313 | − columnDataType = DataType::DateTime; | |
| 390 | + dataType = DataType::DateTime; | |
| 314 | 391 | } |
| 315 | 392 | else if (IsEqual(typeName, "float", false)) |
| 316 | 393 | { |
| 317 | − columnDataType = DataType::Single; | |
| 394 | + dataType = DataType::Single; | |
| 318 | 395 | } |
| 319 | 396 | else if (IsEqual(typeName, "double", false)) |
| 320 | 397 | { |
| 321 | − columnDataType = DataType::Double; | |
| 398 | + dataType = DataType::Double; | |
| 322 | 399 | } |
| 323 | 400 | else if (IsEqual(typeName, "decimal", false)) |
| 324 | 401 | { |
| 325 | − columnDataType = DataType::Decimal; | |
| 402 | + dataType = DataType::Decimal; | |
| 326 | 403 | } |
| 327 | 404 | else if (IsEqual(typeName, "byte", false)) |
| 328 | 405 | { |
| 329 | − columnDataType = DataType::Int8; | |
| 406 | + dataType = DataType::Int8; | |
| 330 | 407 | } |
| 331 | 408 | else if (IsEqual(typeName, "short", false)) |
| 332 | 409 | { |
| 333 | − columnDataType = DataType::Int16; | |
| 410 | + dataType = DataType::Int16; | |
| 334 | 411 | } |
| 335 | 412 | else if (IsEqual(typeName, "int", false)) |
| 336 | 413 | { |
| 337 | − columnDataType = DataType::Int32; | |
| 414 | + dataType = DataType::Int32; | |
| 338 | 415 | } |
| 339 | 416 | else if (IsEqual(typeName, "long", false)) |
| 340 | 417 | { |
| 341 | − columnDataType = DataType::Int64; | |
| 418 | + dataType = DataType::Int64; | |
| 342 | 419 | } |
| 343 | 420 | else if (IsEqual(typeName, "unsignedByte", false)) |
| 344 | 421 | { |
| 345 | − columnDataType = DataType::UInt8; | |
| 422 | + dataType = DataType::UInt8; | |
| 346 | 423 | } |
| 347 | 424 | else if (IsEqual(typeName, "unsignedShort", false)) |
| 348 | 425 | { |
| 349 | − columnDataType = DataType::UInt16; | |
| 426 | + dataType = DataType::UInt16; | |
| 350 | 427 | } |
| 351 | 428 | else if (IsEqual(typeName, "unsignedInt", false)) |
| 352 | 429 | { |
| 353 | − columnDataType = DataType::UInt32; | |
| 430 | + dataType = DataType::UInt32; | |
| 354 | 431 | } |
| 355 | 432 | else if (IsEqual(typeName, "unsignedLong", false)) |
| 356 | 433 | { |
| 357 | − columnDataType = DataType::UInt64; | |
| 434 | + dataType = DataType::UInt64; | |
| 358 | 435 | } |
| 359 | 436 | else |
| 360 | 437 | { |
| @@ -364,116 +441,343 @@ DataSetPtr DataSet::ParseXmlDataSet(const vector<uint8_t>& xmlDataSet) · 3 unmodified lines | ||
| 364 | 441 | } |
| 365 | 442 | |
| 366 | 443 | // Create column |
| 367 | − const DataColumnPtr dataColumn = dataTable->CreateColumn(columnName, columnDataType, columnExpression); | |
| 444 | + const DataColumnPtr dataColumn = dataTable->CreateColumn(columnName, dataType, columnExpression); | |
| 368 | 445 | dataTable->AddColumn(dataColumn); |
| 369 | 446 | } |
| 370 | 447 | |
| 371 | − dataSet->AddOrUpdateTable(dataTable); | |
| 448 | + AddOrUpdateTable(dataTable); | |
| 372 | 449 | } |
| 373 | 450 | |
| 374 | 451 | // Each root node child that matches a table name represents a record |
| 375 | 452 | for (xml_node recordNode = rootNode.first_child(); recordNode; recordNode = recordNode.next_sibling()) |
| 376 | 453 | { |
| 377 | − const DataTablePtr dataTable = dataSet->Table(recordNode.name()); | |
| 454 | + const DataTablePtr table = Table(recordNode.name()); | |
| 378 | 455 | |
| 379 | − if (dataTable == nullptr) | |
| 456 | + if (table == nullptr) | |
| 380 | 457 | continue; |
| 381 | 458 | |
| 382 | − const DataRowPtr dataRow = dataTable->CreateRow(); | |
| 459 | + const DataRowPtr row = table->CreateRow(); | |
| 383 | 460 | |
| 384 | 461 | // Each record node child represents a field value |
| 385 | 462 | for (xml_node fieldNode = recordNode.first_child(); fieldNode; fieldNode = fieldNode.next_sibling()) |
| 386 | 463 | { |
| 387 | − const DataColumnPtr dataColumn = dataTable->Column(fieldNode.name()); | |
| 464 | + const DataColumnPtr column = table->Column(fieldNode.name()); | |
| 388 | 465 | |
| 389 | − if (dataColumn == nullptr) | |
| 466 | + if (column == nullptr) | |
| 390 | 467 | continue; |
| 391 | 468 | |
| 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(); | |
| 394 | 471 | |
| 395 | − switch (dataColumn->Type()) | |
| 472 | + switch (column->Type()) | |
| 396 | 473 | { |
| 397 | 474 | case DataType::String: |
| 398 | − dataRow->SetStringValue(index, string(value.as_string(""))); | |
| 475 | + row->SetStringValue(columnIndex, string(nodeText.as_string(""))); | |
| 399 | 476 | break; |
| 400 | 477 | case DataType::Boolean: |
| 401 | − dataRow->SetBooleanValue(index, value.as_bool()); | |
| 478 | + row->SetBooleanValue(columnIndex, nodeText.as_bool()); | |
| 402 | 479 | break; |
| 403 | 480 | case DataType::DateTime: |
| 404 | − if (value.empty()) | |
| 405 | − dataRow->SetDateTimeValue(index, Empty::DateTime); | |
| 481 | + if (nodeText.empty()) | |
| 482 | + row->SetDateTimeValue(columnIndex, Empty::DateTime); | |
| 406 | 483 | else |
| 407 | − dataRow->SetDateTimeValue(index, ParseTimestamp(value.as_string())); | |
| 484 | + row->SetDateTimeValue(columnIndex, ParseTimestamp(nodeText.as_string())); | |
| 408 | 485 | break; |
| 409 | 486 | case DataType::Single: |
| 410 | − dataRow->SetSingleValue(index, value.as_float()); | |
| 487 | + row->SetSingleValue(columnIndex, nodeText.as_float()); | |
| 411 | 488 | break; |
| 412 | 489 | case DataType::Double: |
| 413 | − dataRow->SetDoubleValue(index, value.as_double()); | |
| 490 | + row->SetDoubleValue(columnIndex, nodeText.as_double()); | |
| 414 | 491 | break; |
| 415 | 492 | 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)); | |
| 418 | 495 | else |
| 419 | − dataRow->SetDecimalValue(index, decimal_t(value.as_string())); | |
| 496 | + row->SetDecimalValue(columnIndex, decimal_t(nodeText.as_string())); | |
| 420 | 497 | break; |
| 421 | 498 | case DataType::Guid: |
| 422 | − if (value.empty()) | |
| 423 | − dataRow->SetGuidValue(index, Empty::Guid); | |
| 499 | + if (nodeText.empty()) | |
| 500 | + row->SetGuidValue(columnIndex, Empty::Guid); | |
| 424 | 501 | else |
| 425 | − dataRow->SetGuidValue(index, ParseGuid(value.as_string())); | |
| 502 | + row->SetGuidValue(columnIndex, ParseGuid(nodeText.as_string())); | |
| 426 | 503 | break; |
| 427 | 504 | 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())); | |
| 429 | 506 | break; |
| 430 | 507 | 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())); | |
| 432 | 509 | break; |
| 433 | 510 | case DataType::Int32: |
| 434 | − dataRow->SetInt32Value(index, value.as_int()); | |
| 511 | + row->SetInt32Value(columnIndex, nodeText.as_int()); | |
| 435 | 512 | break; |
| 436 | 513 | case DataType::Int64: |
| 437 | − dataRow->SetInt64Value(index, value.as_llong()); | |
| 514 | + row->SetInt64Value(columnIndex, nodeText.as_llong()); | |
| 438 | 515 | break; |
| 439 | 516 | 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())); | |
| 441 | 518 | break; |
| 442 | 519 | 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())); | |
| 444 | 521 | break; |
| 445 | 522 | case DataType::UInt32: |
| 446 | − dataRow->SetUInt32Value(index, value.as_uint()); | |
| 523 | + row->SetUInt32Value(columnIndex, nodeText.as_uint()); | |
| 447 | 524 | break; |
| 448 | 525 | case DataType::UInt64: |
| 449 | − dataRow->SetUInt64Value(index, value.as_ullong()); | |
| 526 | + row->SetUInt64Value(columnIndex, nodeText.as_ullong()); | |
| 450 | 527 | break; |
| 451 | 528 | default: |
| 452 | 529 | throw DataSetException("Unexpected column data type encountered"); |
| 453 | 530 | } |
| 454 | 531 | } |
| 455 | 532 | |
| 456 | − dataTable->AddRow(dataRow); | |
| 533 | + table->AddRow(row); | |
| 457 | 534 | } |
| 458 | − | |
| 459 | − return dataSet; | |
| 460 | 535 | } |
| 461 | 536 | |
| 462 | −vector<uint8_t> DataSet::GenerateXmlDataSet(const DataSetPtr& dataSet, const string& dataSetName) | |
| 537 | +void DataSet::GenerateXml(xml_document& document, const string& dataSetName) const | |
| 463 | 538 | { |
| 464 | − xml_document document; | |
| 465 | 539 | 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> | |
| 467 | 555 | xml_node rootNode = document.append_child(rootNodeName); |
| 468 | 556 | |
| 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); | |
| 472 | 559 | schemaNode.append_attribute("id") = rootNodeName; |
| 473 | 560 | schemaNode.append_attribute("xmlns:xs") = XmlSchemaNamespace.c_str(); |
| 474 | 561 | schemaNode.append_attribute("xmlns:ext") = ExtXmlSchemaDataNamespace.c_str(); |
| 475 | 562 | |
| 476 | − vector<uint8_t> xmlDataSet; | |
| 563 | + // <xs:element name="DataSet"> | |
| 564 | + xml_node elementNode = schemaNode.append_child(elementNodeName); | |
| 565 | + elementNode.append_attribute("name") = rootNodeName; | |
| 477 | 566 | |
| 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 | +} | |