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 | using namespace GSF; | 29 | using namespace GSF; |
| 30 | using namespace GSF::Data; | 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 | DataSetException::DataSetException(string message) noexcept : | 52 | DataSetException::DataSetException(string message) noexcept : |
| 33 | m_message(move(message)) | 53 | m_message(move(message)) |
| 34 | { | 54 | { |
| @@ -72,10 +92,24 @@ int32_t DataSet::TableCount() const · 37 unmodified lines | |||
| 72 | return m_tables.size(); | 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 | for (auto const& item : m_tables) | 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 | bool DataSet::AddOrUpdateTable(DataTablePtr table) | 115 | bool DataSet::AddOrUpdateTable(DataTablePtr table) |
| @@ -89,18 +123,61 @@ bool DataSet::RemoveTable(const string& tableName) · 7 unmodified lines | |||
| 89 | return m_tables.erase(tableName) > 0; | 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 | xml_document document; | 128 | xml_document document; |
| 95 | 129 | ||
| 96 | // Load dataset into an XML parser | 130 | const xml_parse_result result = document.load_file(fileName.c_str()); |
| 97 | const xml_parse_result result = document.load_buffer_inplace(const_cast<uint8_t*>(xmlDataSet.data()), xmlDataSet.size()); | ||
| 98 | 131 | ||
| 99 | if (result.status != xml_parse_status::status_ok) | 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 | DataSetPtr dataSet = NewSharedPtr<DataSet>(); | 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 | // Find root node | 181 | // Find root node |
| 105 | xml_node rootNode = document.document_element(); | 182 | xml_node rootNode = document.document_element(); |
| 106 | const string rootNodeName = string(rootNode.name()); | 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 | xml_attribute nameAttribute, typeAttribute, extDataTypeAttribute, extExpressionAttribute; | 236 | xml_attribute nameAttribute, typeAttribute, extDataTypeAttribute, extExpressionAttribute; |
| 160 | 237 | ||
| 161 | // Find element node | 238 | // Find schema element node |
| 162 | xml_node elementNode; | 239 | xml_node elementNode; |
| 163 | const string elementNodeName = schemaPrefix + "element"; | 240 | const string elementNodeName = schemaPrefix + "element"; |
| 164 | 241 | ||
| @@ -241,7 +318,7 @@ DataSetPtr DataSet::ParseXmlDataSet(const vector<uint8_t>& xmlDataSet) · 76 unmodified lines | |||
| 241 | if (!IsEqual(sequenceNode.name(), schemaPrefix + "sequence", false)) | 318 | if (!IsEqual(sequenceNode.name(), schemaPrefix + "sequence", false)) |
| 242 | continue; | 319 | continue; |
| 243 | 320 | ||
| 244 | const DataTablePtr dataTable = NewSharedPtr<DataTable>(dataSet, tableName); | 321 | const DataTablePtr dataTable = CreateTable(tableName); |
| 245 | 322 | ||
| 246 | // Each sequence node child element node represents a table field definition | 323 | // Each sequence node child element node represents a table field definition |
| 247 | for (xml_node node = sequenceNode.first_child(); node; node = node.next_sibling()) | 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 | columnExpression = extExpressionAttribute.value(); | 372 | columnExpression = extExpressionAttribute.value(); |
| 296 | } | 373 | } |
| 297 | 374 | ||
| 298 | DataType columnDataType; | 375 | DataType dataType; |
| 299 | 376 | ||
| 300 | if (IsEqual(typeName, "string", false)) | 377 | if (IsEqual(typeName, "string", false)) |
| 301 | { | 378 | { |
| 302 | if (!extDataType.empty() && StartsWith(extDataType, "System.Guid", false)) | 379 | if (!extDataType.empty() && StartsWith(extDataType, "System.Guid", false)) |
| 303 | columnDataType = DataType::Guid; | 380 | dataType = DataType::Guid; |
| 304 | else | 381 | else |
| 305 | columnDataType = DataType::String; | 382 | dataType = DataType::String; |
| 306 | } | 383 | } |
| 307 | else if (IsEqual(typeName, "boolean", false)) | 384 | else if (IsEqual(typeName, "boolean", false)) |
| 308 | { | 385 | { |
| 309 | columnDataType = DataType::Boolean; | 386 | dataType = DataType::Boolean; |
| 310 | } | 387 | } |
| 311 | else if (IsEqual(typeName, "dateTime", false)) | 388 | else if (IsEqual(typeName, "dateTime", false)) |
| 312 | { | 389 | { |
| 313 | columnDataType = DataType::DateTime; | 390 | dataType = DataType::DateTime; |
| 314 | } | 391 | } |
| 315 | else if (IsEqual(typeName, "float", false)) | 392 | else if (IsEqual(typeName, "float", false)) |
| 316 | { | 393 | { |
| 317 | columnDataType = DataType::Single; | 394 | dataType = DataType::Single; |
| 318 | } | 395 | } |
| 319 | else if (IsEqual(typeName, "double", false)) | 396 | else if (IsEqual(typeName, "double", false)) |
| 320 | { | 397 | { |
| 321 | columnDataType = DataType::Double; | 398 | dataType = DataType::Double; |
| 322 | } | 399 | } |
| 323 | else if (IsEqual(typeName, "decimal", false)) | 400 | else if (IsEqual(typeName, "decimal", false)) |
| 324 | { | 401 | { |
| 325 | columnDataType = DataType::Decimal; | 402 | dataType = DataType::Decimal; |
| 326 | } | 403 | } |
| 327 | else if (IsEqual(typeName, "byte", false)) | 404 | else if (IsEqual(typeName, "byte", false)) |
| 328 | { | 405 | { |
| 329 | columnDataType = DataType::Int8; | 406 | dataType = DataType::Int8; |
| 330 | } | 407 | } |
| 331 | else if (IsEqual(typeName, "short", false)) | 408 | else if (IsEqual(typeName, "short", false)) |
| 332 | { | 409 | { |
| 333 | columnDataType = DataType::Int16; | 410 | dataType = DataType::Int16; |
| 334 | } | 411 | } |
| 335 | else if (IsEqual(typeName, "int", false)) | 412 | else if (IsEqual(typeName, "int", false)) |
| 336 | { | 413 | { |
| 337 | columnDataType = DataType::Int32; | 414 | dataType = DataType::Int32; |
| 338 | } | 415 | } |
| 339 | else if (IsEqual(typeName, "long", false)) | 416 | else if (IsEqual(typeName, "long", false)) |
| 340 | { | 417 | { |
| 341 | columnDataType = DataType::Int64; | 418 | dataType = DataType::Int64; |
| 342 | } | 419 | } |
| 343 | else if (IsEqual(typeName, "unsignedByte", false)) | 420 | else if (IsEqual(typeName, "unsignedByte", false)) |
| 344 | { | 421 | { |
| 345 | columnDataType = DataType::UInt8; | 422 | dataType = DataType::UInt8; |
| 346 | } | 423 | } |
| 347 | else if (IsEqual(typeName, "unsignedShort", false)) | 424 | else if (IsEqual(typeName, "unsignedShort", false)) |
| 348 | { | 425 | { |
| 349 | columnDataType = DataType::UInt16; | 426 | dataType = DataType::UInt16; |
| 350 | } | 427 | } |
| 351 | else if (IsEqual(typeName, "unsignedInt", false)) | 428 | else if (IsEqual(typeName, "unsignedInt", false)) |
| 352 | { | 429 | { |
| 353 | columnDataType = DataType::UInt32; | 430 | dataType = DataType::UInt32; |
| 354 | } | 431 | } |
| 355 | else if (IsEqual(typeName, "unsignedLong", false)) | 432 | else if (IsEqual(typeName, "unsignedLong", false)) |
| 356 | { | 433 | { |
| 357 | columnDataType = DataType::UInt64; | 434 | dataType = DataType::UInt64; |
| 358 | } | 435 | } |
| 359 | else | 436 | else |
| 360 | { | 437 | { |
| @@ -364,116 +441,343 @@ DataSetPtr DataSet::ParseXmlDataSet(const vector<uint8_t>& xmlDataSet) · 3 unmodified lines | |||
| 364 | } | 441 | } |
| 365 | 442 | ||
| 366 | // Create column | 443 | // Create column |
| 367 | const DataColumnPtr dataColumn = dataTable->CreateColumn(columnName, columnDataType, columnExpression); | 444 | const DataColumnPtr dataColumn = dataTable->CreateColumn(columnName, dataType, columnExpression); |
| 368 | dataTable->AddColumn(dataColumn); | 445 | dataTable->AddColumn(dataColumn); |
| 369 | } | 446 | } |
| 370 | 447 | ||
| 371 | dataSet->AddOrUpdateTable(dataTable); | 448 | AddOrUpdateTable(dataTable); |
| 372 | } | 449 | } |
| 373 | 450 | ||
| 374 | // Each root node child that matches a table name represents a record | 451 | // Each root node child that matches a table name represents a record |
| 375 | for (xml_node recordNode = rootNode.first_child(); recordNode; recordNode = recordNode.next_sibling()) | 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 | continue; | 457 | continue; |
| 381 | 458 | ||
| 382 | const DataRowPtr dataRow = dataTable->CreateRow(); | 459 | const DataRowPtr row = table->CreateRow(); |
| 383 | 460 | ||
| 384 | // Each record node child represents a field value | 461 | // Each record node child represents a field value |
| 385 | for (xml_node fieldNode = recordNode.first_child(); fieldNode; fieldNode = fieldNode.next_sibling()) | 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 | continue; | 467 | continue; |
| 391 | 468 | ||
| 392 | const int32_t index = dataColumn->Index(); | 469 | const int32_t columnIndex = column->Index(); |
| 393 | const xml_text value = fieldNode.text(); | 470 | const xml_text nodeText = fieldNode.text(); |
| 394 | 471 | ||
| 395 | switch (dataColumn->Type()) | 472 | switch (column->Type()) |
| 396 | { | 473 | { |
| 397 | case DataType::String: | 474 | case DataType::String: |
| 398 | dataRow->SetStringValue(index, string(value.as_string(""))); | 475 | row->SetStringValue(columnIndex, string(nodeText.as_string(""))); |
| 399 | break; | 476 | break; |
| 400 | case DataType::Boolean: | 477 | case DataType::Boolean: |
| 401 | dataRow->SetBooleanValue(index, value.as_bool()); | 478 | row->SetBooleanValue(columnIndex, nodeText.as_bool()); |
| 402 | break; | 479 | break; |
| 403 | case DataType::DateTime: | 480 | case DataType::DateTime: |
| 404 | if (value.empty()) | 481 | if (nodeText.empty()) |
| 405 | dataRow->SetDateTimeValue(index, Empty::DateTime); | 482 | row->SetDateTimeValue(columnIndex, Empty::DateTime); |
| 406 | else | 483 | else |
| 407 | dataRow->SetDateTimeValue(index, ParseTimestamp(value.as_string())); | 484 | row->SetDateTimeValue(columnIndex, ParseTimestamp(nodeText.as_string())); |
| 408 | break; | 485 | break; |
| 409 | case DataType::Single: | 486 | case DataType::Single: |
| 410 | dataRow->SetSingleValue(index, value.as_float()); | 487 | row->SetSingleValue(columnIndex, nodeText.as_float()); |
| 411 | break; | 488 | break; |
| 412 | case DataType::Double: | 489 | case DataType::Double: |
| 413 | dataRow->SetDoubleValue(index, value.as_double()); | 490 | row->SetDoubleValue(columnIndex, nodeText.as_double()); |
| 414 | break; | 491 | break; |
| 415 | case DataType::Decimal: | 492 | case DataType::Decimal: |
| 416 | if (value.empty()) | 493 | if (nodeText.empty()) |
| 417 | dataRow->SetDecimalValue(index, decimal_t(0)); | 494 | row->SetDecimalValue(columnIndex, decimal_t(0)); |
| 418 | else | 495 | else |
| 419 | dataRow->SetDecimalValue(index, decimal_t(value.as_string())); | 496 | row->SetDecimalValue(columnIndex, decimal_t(nodeText.as_string())); |
| 420 | break; | 497 | break; |
| 421 | case DataType::Guid: | 498 | case DataType::Guid: |
| 422 | if (value.empty()) | 499 | if (nodeText.empty()) |
| 423 | dataRow->SetGuidValue(index, Empty::Guid); | 500 | row->SetGuidValue(columnIndex, Empty::Guid); |
| 424 | else | 501 | else |
| 425 | dataRow->SetGuidValue(index, ParseGuid(value.as_string())); | 502 | row->SetGuidValue(columnIndex, ParseGuid(nodeText.as_string())); |
| 426 | break; | 503 | break; |
| 427 | case DataType::Int8: | 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 | break; | 506 | break; |
| 430 | case DataType::Int16: | 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 | break; | 509 | break; |
| 433 | case DataType::Int32: | 510 | case DataType::Int32: |
| 434 | dataRow->SetInt32Value(index, value.as_int()); | 511 | row->SetInt32Value(columnIndex, nodeText.as_int()); |
| 435 | break; | 512 | break; |
| 436 | case DataType::Int64: | 513 | case DataType::Int64: |
| 437 | dataRow->SetInt64Value(index, value.as_llong()); | 514 | row->SetInt64Value(columnIndex, nodeText.as_llong()); |
| 438 | break; | 515 | break; |
| 439 | case DataType::UInt8: | 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 | break; | 518 | break; |
| 442 | case DataType::UInt16: | 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 | break; | 521 | break; |
| 445 | case DataType::UInt32: | 522 | case DataType::UInt32: |
| 446 | dataRow->SetUInt32Value(index, value.as_uint()); | 523 | row->SetUInt32Value(columnIndex, nodeText.as_uint()); |
| 447 | break; | 524 | break; |
| 448 | case DataType::UInt64: | 525 | case DataType::UInt64: |
| 449 | dataRow->SetUInt64Value(index, value.as_ullong()); | 526 | row->SetUInt64Value(columnIndex, nodeText.as_ullong()); |
| 450 | break; | 527 | break; |
| 451 | default: | 528 | default: |
| 452 | throw DataSetException("Unexpected column data type encountered"); | 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 | const char* rootNodeName = dataSetName.c_str(); | 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 | xml_node rootNode = document.append_child(rootNodeName); | 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"> | 557 | // <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"); | 558 | xml_node schemaNode = rootNode.append_child(schemaNodeName); |
| 471 | |||
| 472 | schemaNode.append_attribute("id") = rootNodeName; | 559 | schemaNode.append_attribute("id") = rootNodeName; |
| 473 | schemaNode.append_attribute("xmlns:xs") = XmlSchemaNamespace.c_str(); | 560 | schemaNode.append_attribute("xmlns:xs") = XmlSchemaNamespace.c_str(); |
| 474 | schemaNode.append_attribute("xmlns:ext") = ExtXmlSchemaDataNamespace.c_str(); | 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; | 567 | // <xs:complexType> |
| 479 | } | 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 | } | ||