在Qt C++中序列化/解析一个文件中的多个对象

Serializing/parsing multiple objects in one file in Qt C++

本文关键字:一个 文件 对象 C++ Qt 序列化      更新时间:2023-10-16

我需要序列化和解析项目中的多个对象,以便在需要时保存/加载它们。

我的对象将具有完全相同的组件:一个 QString 名称、一个整数 id、一个 QString 描述和两个整数 xy。 我需要这样的东西:

{"name":"toto", "id":"42", "description":"tata", "x":"20", "y":"50"}

所以我会像这样构建我的QJsonObject:

QJsonObject json;
json["id"] = object_to_serialize.get_id();
json["name"] = object_to_serialize.get_name();
json["description"] = object_to_serialize.get_description();
json["x"] = object_to_serialize.get_x();
json["y"] = object_to_serialize.get_y();
QJsonDocument filedoc(json);
file.write(filedoc.toJson);`

在文件中,它将如下所示:

{"name":"toto", "id":"42", "description":"tata", "x":"20", "y":"50"}
{"name":"toto2", "id":"44", "description":"tata2", "x":"25", "y":"547"}  
{"name":"toto3", "id":"46", "description":"tata3", "x":"21", "y":"580"}

我的序列化程序将接收参数对象、保存文件名,并将对象转换为 QJsonObject。然后,它将需要读取文件以检查此处是否存在具有相同 id 的对象。如果它在这里,它将需要替换它,如果它不是,它将附加它。

我在序列化选项和如何阅读它之间有点迷茫;

我应该制作一个包含多个 QJsonObject 的 QJsonArray 还是使用 QJsonArrays 的 QJsonObject ?

当我阅读它时,我需要检查 id ; 但会

foreach(object.value["id"] == 42)
  //create the QJsonObject from the one with 42 and change it with the new data

将解析对象而不是全部?有没有更好的方法?

提前感谢您的回答。

你可以有一个 json 对象的数组,每个对象都有一个 ID,以便你可以解析相关的对象。

虽然你也可以解析所有这些文件并将它们添加到地图中,但只要你没有很重的文件,它应该没问题。

void parseJson(const QString &data)
{
    QJsonDocument doc = QJsonDocument::fromJson(data.toUtf8());
    if (doc.isNull())
    {
        war("invalid json document");
        return;
    }

    QJsonArray jsonArray = doc.array();
    foreach (const QJsonValue & value, jsonArray) {
        QJsonObject obj = value.toObject();
        if (obj.contains("id"))
        {
            if (obj["id"].toInt() == yourId) parseObject(obj);
        }
    }
}

void parseObject(const QJsonObject &obj)
{
    if (obj.contains("valueA")) valueA = obj["valueA"].toDouble();
    if (obj.contains("valueB")) valueB = obj["valueB"].toDouble();
}

如果您的文件不是太大,这将正常工作

>更大的文件

现在,如果您有非常大的文件,将其全部加载到内存中并解析它可能会有问题。

由于您的结构始终相同且非常简单,因此 JSON 可能不是最佳选择,一种更有效的方法是执行您自己的解析器(或使用一些现有的解析器(,它可以读取文件并将其作为流处理。


另一种方法是每行有一个 JSON 条目,前面有一个固定位数的 ID。在 QHash 查找中加载它,然后只从文件中读取感兴趣的 id,并且只解析一小部分。

// This code is not tested and is just to show the principle.
#define IDSIZE 5
QHash<int64, int64> m_lookup; // has to be global var
// For very large file, this might take some time and can be done on a separate thread.
// it needs to be done only once at startup (given the file is not modified externally)
void createLookup(const QString &fileName)
{
    QFile inputFile(fileName);
    if (inputFile.open(QIODevice::ReadOnly))
    {
       QTextStream in(&inputFile);
       while (!in.atEnd())
       {
          int position = in.pos(); // store the position in the file
          QString line = in.readLine();
          int id = line.mid(0,IDSIZE).toInt(); // 5 digit id (like 00001, 00002, etc...
          m_lookup[id] = position + IDSIZE;
       }
       inputFile.close();
    }
}
QString getEntry(const QString &fileName, int64 id)
{
     if (m_lookup.contains(id))
     {
        QFile inputFile(fileName);
        if (inputFile.open(QIODevice::ReadOnly))
        {
            inputFile.seek(m_lookup[id]);
            QString data = inputFile.readLine();
            inputFile.close();
            return data;
        } else {
            return QString(); // or handle error
        }
     } else {
        return QString(); // or handle error
     }
}
// use example
QString data = getEntry(id);
if (data.length() > 0)
{
    QJsonDocument doc = QJsonDocument::fromJson(data.toUtf8());
    if (!doc.isNull())
    {
        // assign your variables
    }
}

您的数据文件如下所示:

00042{"name":"toto", "id":"42", "description":"tata", "x":"20", "y":"50"}
00044{"name":"toto2", "id":"44", "description":"tata2", "x":"25", "y":"547"}  
00046{"name":"toto3", "id":"46", "description":"tata3", "x":"21", "y":"580"}

这种方法的优点是,它只会读取感兴趣的条目,而不必为了获取特定条目而在内存中加载MB或GB的数据。

这可以通过使用存储在文件开头的查找表来进一步改进。

相关文章: