Ошибки с возвратом типов данных, отличных от char*, из XML-файла с использованием C++

Я впервые использую XML, и в настоящее время я пытаюсь вернуть целое число (на самом деле хочу вернуть двойное число, но еще не зашло так далеко) из XML-файла с использованием С++. Я использую RAPIDXML и следующую реализацию:

Все файлы находятся в одном каталоге.

XML (firstxml.xml):

<?xml version="1.0"?>
<test xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xsi:noNamespaceSchemaLocation="firstxsd.xsd">
    <A>10</A>
    <B>Hello</B>
</test>

XML-схема (firstxsd.xsd):

<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="test">
    <xs:complexType>
      <xs:sequence>
        <xs:element type="xs:integer" name="A"/>
        <xs:element type="xs:string" name="B"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

С++ (test.cxx):

#include <iostream>
#include <sstream>
#include <fstream>
#include "rapidxml-1.13/rapidxml.hpp"
#include "rapidxml-1.13/rapidxml_print.hpp"
#include <string>
#include <stdio.h>
#include <vector>

int main(int argc, char* argv[])
  {
    std::ifstream file ("firstxml.xml");
      if (file.is_open())
        {
          file.seekg(0,std::ios::end);
          int size = file.tellg();
          file.seekg(0,std::ios::beg);

          char* buffer = new char [size];

          file.read (buffer, size);
          file.close();

          rapidxml::xml_document<> doc;
          doc.parse<0>(buffer);
          rapidxml::xml_node<> *node = doc.first_node()->first_node();

          //Line which results in error
          std::cout << node->value()*10 << std::endl;

          delete[] buffer;
        }
  }

Ошибка:

test.cxx:52:31: error: invalid operands of types ‘char*’ and ‘int’ to binary ‘operator*’

Из руководств, которые я прочитал в Интернете, я считаю, что правильно создаю файлы, поэтому значение, проанализированное в файле C++ из узла A, должно быть целым числом. Я заметил одну вещь: в руководстве по RAPIDXML спецификация value() выглядит следующим образом:

Ch* value() const;

Description: Gets value of node. Interpretation of value depends on type of node. Note that value will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. Use value_size() function to determine length of the value.

Returns: Value of node, or empty string if node has no value.

Таким образом, определение функции говорит, что она всегда возвращает указатель на символ, но строка «Интерпретация значения зависит от типа узла» подразумевает, что она возвращает значение, зависящее от типа.

Спасибо, что нашли время, чтобы посмотреть на мою проблему, любая помощь очень ценится,

Павел.


person Paul    schedule 26.02.2013    source источник
comment
С++ не различает функции на основе возвращаемого значения, поэтому не может быть перегрузки value(), которая возвращает что-то еще, если только вы не добавите дополнительные аргументы, позволяющие С++ видеть их как разные (шаблоны — это другое, но, по-видимому, здесь не используется) .   -  person hyde    schedule 26.02.2013


Ответы (3)


Трудно сказать, что означает фраза «Интерпретация значения зависит от типа узла». Если функция возвращает Ch pointer, она всегда будет возвращать такой указатель или вызывать исключение времени выполнения, если разработчики библиотеки предусмотрели такую ​​возможность. Естественно, когда вы пытаетесь умножить Ch* на int, вы получаете ошибку времени компиляции.

Единственный способ, которым функция может вернуть тип, зависящий от контекста вызова, — это использовать какой-то специальный пользовательский тип возвращаемого значения (VARIANT или что-то подобное). Мне кажется, что Ch — это просто псевдоним для основного типа символов библиотеки (char или, может быть, wchar_t).

Таким образом, под этим уведомлением об "интерпретации..." разработчики, вероятно, имели в виду, что пользователь должен правильно интерпретировать строку символов в соответствии с типом узла (который, в свою очередь, зависит от определяемой пользователем схемы).

Чтобы преобразовать C-строковое представление числа в соответствующие типы, вы можете использовать функции из стандартной библиотеки C, такие как atoi() (преобразует в int), atof() (в double) и так далее.

person Pavel Zhuravlev    schedule 26.02.2013
comment
Ах! Это сработало отлично, большое спасибо за быстрый и подробный ответ! - person Paul; 26.02.2013

Я думаю, вы хотите получить доступ к значению «атрибута», поэтому сделайте что-то вроде...

cout ‹‹ "Имя моего первого узла: " ‹‹ doc.first_node()->name() ‹‹ "\n";

xml_node‹> *node = doc.first_node("foobar");

cout ‹‹ "Узел foobar имеет значение" ‹‹ node->value() ‹‹ "\n";

for (xml_attribute‹> *attr = node->first_attribute(); attr; attr = attr->next_attribute())

{ cout ‹‹ "Узел foobar имеет атрибут " ‹‹ attr->name() ‹‹ " ";

cout << "with value " << attr->value() << "\n";

}

А также взгляните на учебник по rapidXML...

http://rapidxml.sourceforge.net/manual.html это просто!

person Saqlain    schedule 26.02.2013
comment
Спасибо за ответ, однако я не верю, что значение считается атрибутом узла, поскольку оно является элементом узла? Я добавил в xml следующее: ‹int num=5›10‹/int› И в xsd: ‹xs:attribute name=num type=xs:integer/› Однако это по-прежнему вызывает ту же ошибку, что и раньше, при вызове атрибут. Спасибо, Пол. - person Paul; 26.02.2013

Кто-то поправит меня, если я ошибаюсь, я считаю, что RapidXML будет давать вам значения только в виде некоторой формы строки из-за его синтаксического анализа на месте. Шаблон класса может быть для обработки char против широкого char или что-то в этом роде, я не знаю, я использую только char.

Что я сделал для обработки извлечения значений, так это добавил вспомогательный класс в rapidxml_utils.hpp (действительно мог бы поместить его куда угодно, но там это имело смысл) с некоторыми статическими функциями для анализа узла или атрибута xml в значение (или значение по умолчанию, если он не анализируется).

Эти помощники позволили мне делать такие вещи, как:

    int Height = xml_helper::GetNodeInt(cur_node->first_node("Height"), 48);
    int Width = xml_helper::GetNodeInt(cur_node->first_attribute("Width"), 128);

В вашем случае вы можете использовать мой вспомогательный класс следующим образом:

      //Line which results in error (altered to use my helper class, if node absent or not parse as integer uses zero)
      std::cout << xml_helper::GetNodeInt(node, 0)*10 << std::endl;

Мой вспомогательный класс выглядит так (я использовал sscanf, чтобы получить некоторый уровень проверки):

class xml_helper
{
public:
    // a couple little helpers, lets you do this:
    // int Height = xml_helper::GetNodeInt(cur_node->first_node("Height"), 48);
    // int Width = xml_helper::GetNodeInt(cur_node->first_node("Width"), 128);
    static int GetNodeInt(rapidxml::xml_base<>* node, int DefaultValue)
    {
        int temp;
        try
        {
            if (node == 0) return DefaultValue;
            if (sscanf(node->value(), "%d", &temp) != 1) return DefaultValue;
            return temp;
            //return atoi(node->value());
        }
        catch (...) { return DefaultValue; }
    }

    static unsigned int GetNodeUInt(rapidxml::xml_base<>* node, unsigned int DefaultValue)
    {
        unsigned int temp;
        try
        {
            if (node == 0) return DefaultValue;
            if (sscanf(node->value(), "%u", &temp) != 1) return DefaultValue;
            return temp;
            //return strtoul(node->value(), NULL, 0);
        }
        catch (...) { return DefaultValue; }
    }

    static long long GetNodeLL(rapidxml::xml_base<>* node, long long DefaultValue)
    {
        long long temp;
        try
        {
            if (node == 0) return DefaultValue;
            if (sscanf(node->value(), "%lld", &temp) != 1) return DefaultValue; //C99 covers %lld
            return temp;
            //return strtoll(node->value(), NULL, 0); //C++11, could use _strtoi64 in VS until then?
        }
        catch (...) { return DefaultValue; }
    }

    static double GetNodeDouble(rapidxml::xml_base<>* node, double DefaultValue)
    {
        double temp;
        try
        {
            if (node == 0) return DefaultValue;
            if (sscanf(node->value(), "%lf", &temp) != 1) return DefaultValue; // could use strtod
            return temp;
        }
        catch (...) { return DefaultValue; }
    }

    static std::string GetNodeStr(rapidxml::xml_base<>* node, std::string DefaultValue)
    {
        try
        {
            if (node == 0) return DefaultValue;
            return node->value();
        }
        catch (...) { return DefaultValue; }
    }
};

Надеюсь, это может быть полезно для вас или кого-то еще, использующего rapidxml и нуждающегося в быстром способе получить целые числа, двойные числа или что-то еще из узлов. Его, безусловно, можно расширить до других типов, сейчас у меня есть только те типы, которые мне нужны, но вы поняли суть.

person CFD    schedule 24.10.2013