Skip to content

File import and export

victor edited this page Aug 12, 2025 · 7 revisions

๐Ÿ›๏ธSmart_Store API guide

Smart_Store enables seamless export and import of item data for persistence, backup, and system integration. Items registered in ItemManager can be exported to JSON or XML formats, each tagged uniquely and embedded with type information to ensure accurate deserialization. Thread safety is maintained through internal mutexes, and only properly registered items are included; hard-coded or invalid entries are discarded. For export, custom types must implement serialization logic (e.g., toJson()), and singleton managers should be flushed to avoid stale data. Undo/redo history is excluded unless configured. Importing supports both bulk restoration and single-object retrieval using typeid, which ensures runtime type safety and avoids issues with mangled names or renamed classes. To maximize reliability, validate exported files, use consistent tag naming, schedule regular backups, and log operations for traceability.


Export and import APIs for JSON:

#include "t_manager/ItemManager.h"
#include "err_log/Logger.hpp"
#include <iostream>
#include <string>

using namespace std;

class HumanBeing {
public:
    virtual void speak() const = 0;
    virtual void walk() const = 0;
    virtual void eat() const = 0;
    virtual ~HumanBeing() = default;
};

// Example derived class 
class Boy : public HumanBeing {
public:
    std::string name;
    int age;

public:
    // If you have a constructor that takes parameters, make sure to define 
    // the default constructor as well to avoid issues.
    Boy(std::string name, int age): name(name), age(age) {}
    Boy() {}
    ~Boy() {}

    void speak() const override {
        LOG_CONTEXT(LogLevel::DISPLAY, "Hello, I am a boy named: " + name +  " I am " + to_string(age) + " years old.", {});
    }

    void walk() const override {
        LOG_CONTEXT(LogLevel::DISPLAY, "I am walking like a boy named " + name+ ".", {});
    }

    void eat() const override {
        LOG_CONTEXT(LogLevel::DISPLAY, "I am eating like a boy named " + name + ".", {});
    }
};

// Example of a girl
class Girl : public HumanBeing {
public:
    std::string name;
    int age;

public:
    // If you have a constructor that takes parameters, make sure to define 
    // the default constructor as well to avoid issues.
    Girl(std::string name, int age): name(name), age(age) {}
    Girl() {}
    ~Girl() {}
   
    void speak() const override {
        LOG_CONTEXT(LogLevel::DISPLAY, "Hello, I am a girl named: " + name +  " I am " + to_string(age) + " years old.", {});
    }

    void walk() const override {
        LOG_CONTEXT(LogLevel::DISPLAY, "I am walking like a girl named " + name + ".", {});
    }

    void eat() const override {
        LOG_CONTEXT(LogLevel::DISPLAY, "I am eating like a girl named " + name + ".", {});
    }
};

int main() {
    ItemManager manager;

    // Add items using constructor
    auto Item1 = std::make_shared<Boy>("Tom", 12);
    auto Item2 = std::make_shared<Girl>("Alice", 10);
    manager.addItem(Item1, "Boy1");
    manager.addItem(Item2, "Girl1");
    LOG_CONTEXT(LogLevel::INFO, "Exporting all items to JSON file.", {});

    manager.exportToFile_Json("items.json");
    std::cout << "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\n";
    LOG_CONTEXT(LogLevel::INFO, "Importing all items from JSON file.", {});

    /* 
       The imports of each item are validated by ID โ€” either self-defined or auto-generated.
       Hard-coded items that are not properly registered in the system will be discarded during import.
       Only items with valid registration and metadata are eligible for deserialization.
    */
    manager.importFromFile_Json("items.json");
    std::cout << "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\n";
    LOG_CONTEXT(LogLevel::INFO, "Importing single object from JSON file.", {});
    
   /*
    You might be wondering why `typeid` is used here.
    The `typeid` operator retrieves type information at runtime, which is critical for dynamic type checking.
    During single-object import, this ensures that the correct class type is used for deserialization.

    Each exported item is tagged with its compiler-generated type name.
    This name is used to match the correct type during import.
    This approach is essential for complex types like `std::string`, `std::vector`, and other STL containers,
    where relying on mangled names or manual identifiers can lead to deserialization errors.

    By using `typeid`, Smart_Store guarantees type-safe imports โ€” even if class names change or evolve.
   */
    manager.importSingleObject_Json("items.json", typeid(Boy).name(), "Boy1");

    return 0;
}

Output for JSON export:

output for json export

Output for JSON imports:

output for json imports

Output for JSON single import:

output for json single import

Export and import APIs for XML:

#include "t_manager/ItemManager.h"
#include "err_log/Logger.hpp"
#include <iostream>
#include <string>

using namespace std;

class HumanBeing {
public:
    virtual void speak() const = 0;
    virtual void walk() const = 0;
    virtual void eat() const = 0;
    virtual ~HumanBeing() = default;
};

// Example derived class 
class Boy : public HumanBeing {
public:
    std::string name;
    int age;

public:
    // If you have a constructor that takes parameters, make sure to define 
    // the default constructor as well to avoid issues.
    Boy(std::string name, int age): name(name), age(age) {}
    Boy() {}
    ~Boy() {}

    void speak() const override {
        LOG_CONTEXT(LogLevel::DISPLAY, "Hello, I am a boy named: " + name +  " I am " + to_string(age) + " years old.", {});
    }

    void walk() const override {
        LOG_CONTEXT(LogLevel::DISPLAY, "I am walking like a boy named " + name+ ".", {});
    }

    void eat() const override {
        LOG_CONTEXT(LogLevel::DISPLAY, "I am eating like a boy named " + name + ".", {});
    }
};

// Example of a girl
class Girl : public HumanBeing {
public:
    std::string name;
    int age;

public:
    // If you have a constructor that takes parameters, make sure to define 
    // the default constructor as well to avoid issues.
    Girl(std::string name, int age): name(name), age(age) {}
    Girl() {}
    ~Girl() {}
   
    void speak() const override {
        LOG_CONTEXT(LogLevel::DISPLAY, "Hello, I am a girl named: " + name +  " I am " + to_string(age) + " years old.", {});
    }

    void walk() const override {
        LOG_CONTEXT(LogLevel::DISPLAY, "I am walking like a girl named " + name + ".", {});
    }

    void eat() const override {
        LOG_CONTEXT(LogLevel::DISPLAY, "I am eating like a girl named " + name + ".", {});
    }
};

int main() {
    ItemManager manager;

    // Add items using constructor
    auto Item1 = std::make_shared<Boy>("Tom", 12);
    auto Item2 = std::make_shared<Girl>("Alice", 10);
    manager.addItem(Item1, "Boy1");
    manager.addItem(Item2, "Girl1");
    LOG_CONTEXT(LogLevel::INFO, "Exporting all items to XML file.", {});

    manager.exportToFile_XML("items.xml");
    std::cout << "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\n";
    LOG_CONTEXT(LogLevel::INFO, "Importing all items from XML file.", {});

     /* 
        The imports of each item are validated by ID โ€” either self-defined or auto-generated.
        Hard-coded items that are not properly registered in the system will be discarded during import.
        Only items with valid registration and metadata are eligible for deserialization.
    */
    manager.importFromFile_XML("items.xml");
    std::cout << "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\n";
    LOG_CONTEXT(LogLevel::INFO, "Importing single object from XML file.", {});

   /*
    You might be wondering why `typeid` is used here.
    The `typeid` operator retrieves type information at runtime, which is critical for dynamic type checking.
    During single-object import, this ensures that the correct class type is used for deserialization.

    Each exported item is tagged with its compiler-generated type name.
    This name is used to match the correct type during import.
    This approach is especially important for complex types like `std::string`, `std::vector`, and other STL containers,
    where relying on mangled names or manual identifiers can lead to deserialization errors.

    By using `typeid`, Smart_Store guarantees type-safe imports โ€” even if class names change or evolve over time.
   */
    manager.importSingleObject_XML("items.xml", typeid(Boy).name(), "Boy1");

    return 0;
}

Output for XML export:

output for XML export

Output for XML imports:

output for XML imports

Output for XML single import:

output for XML single import

Export and import APIs for BINARIES:

#include "t_manager/ItemManager.h"
#include "err_log/Logger.hpp"
#include <iostream>
#include <string>

using namespace std;

class HumanBeing {
public:
    virtual void speak() const = 0;
    virtual void walk() const = 0;
    virtual void eat() const = 0;
    virtual ~HumanBeing() = default;
};

// Example derived class 
class Boy : public HumanBeing {
public:
    std::string name;
    int age;

public:
    // If you have a constructor that takes parameters, make sure to define 
    // the default constructor as well to avoid issues.
    Boy(std::string name, int age): name(name), age(age) {}
    Boy() {}
    ~Boy() {}

    void speak() const override {
        LOG_CONTEXT(LogLevel::DISPLAY, "Hello, I am a boy named: " + name +  " I am " + to_string(age) + " years old.", {});
    }

    void walk() const override {
        LOG_CONTEXT(LogLevel::DISPLAY, "I am walking like a boy named " + name+ ".", {});
    }

    void eat() const override {
        LOG_CONTEXT(LogLevel::DISPLAY, "I am eating like a boy named " + name + ".", {});
    }
};

// Example of a girl
class Girl : public HumanBeing {
public:
    std::string name;
    int age;

public:
    // If you have a constructor that takes parameters, make sure to define 
    // the default constructor as well to avoid issues.
    Girl(std::string name, int age): name(name), age(age) {}
    Girl() {}
    ~Girl() {}
   
    void speak() const override {
        LOG_CONTEXT(LogLevel::DISPLAY, "Hello, I am a girl named: " + name +  " I am " + to_string(age) + " years old.", {});
    }

    void walk() const override {
        LOG_CONTEXT(LogLevel::DISPLAY, "I am walking like a girl named " + name + ".", {});
    }

    void eat() const override {
        LOG_CONTEXT(LogLevel::DISPLAY, "I am eating like a girl named " + name + ".", {});
    }
};

int main() {
    ItemManager manager;

    // Add items using constructor
    auto Item1 = std::make_shared<Boy>("Tom", 12);
    auto Item2 = std::make_shared<Girl>("Alice", 10);
    manager.addItem(Item1, "Boy1");
    manager.addItem(Item2, "Girl1");
    LOG_CONTEXT(LogLevel::INFO, "Exporting all items to XML file.", {});

    manager.exportToFile_Binary("items.bin");
    std::cout << "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\n";
    LOG_CONTEXT(LogLevel::INFO, "Importing all items from Binary file.", {});

    /* 
       The imports of each item are validated by ID โ€” either self-defined or auto-generated.
       Hard-coded items that are not properly registered in the system will be discarded during import.
       Only items with valid registration and metadata are eligible for deserialization.
    */
    manager.importFromFile_Binary("items.bin");
    std::cout << "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\n";
    LOG_CONTEXT(LogLevel::INFO, "Importing single object from Binary file.", {});

   /*
    You might be wondering why `typeid` is used here.
    The `typeid` operator retrieves type information at runtime, which is critical for dynamic type checking.
    During single-object import, this ensures that the correct class type is used for deserialization.

    Each exported item is tagged with its compiler-generated type name.
    This name is used to match the correct type during import.
    This approach is especially important for complex types like `std::string`, `std::vector`, and other STL containers,
    where relying on mangled names or manual identifiers can lead to deserialization errors.

    By using `typeid`, Smart_Store guarantees type-safe imports โ€” even if class names change or evolve over time.
   */
    manager.importSingleObject_Binary("items.bin", typeid(Boy).name(), "Boy1");

    return 0;
}

Output for BINARIES export:

output for BINARIES export

Output for BINARIES imports:

output for BINARIES imports

Output for BINARIES single import:

output for BINARIES single import

Export and import APIs for CSV:

#include "t_manager/ItemManager.h"
#include "err_log/Logger.hpp"
#include <iostream>
#include <string>

using namespace std;

class HumanBeing {
public:
    virtual void speak() const = 0;
    virtual void walk() const = 0;
    virtual void eat() const = 0;
    virtual ~HumanBeing() = default;
};

// Example derived class 
class Boy : public HumanBeing {
public:
    std::string name;
    int age;

public:
    // If you have a constructor that takes parameters, make sure to define 
    // the default constructor as well to avoid issues.
    Boy(std::string name, int age): name(name), age(age) {}
    Boy() : name("Unknown"), age(0) {}

    ~Boy() {}

    void speak() const override {
        LOG_CONTEXT(LogLevel::DISPLAY, "Hello, I am a boy named: " + name +  " I am " + to_string(age) + " years old.", {});
    }

    void walk() const override {
        LOG_CONTEXT(LogLevel::DISPLAY, "I am walking like a boy named " + name+ ".", {});
    }

    void eat() const override {
        LOG_CONTEXT(LogLevel::DISPLAY, "I am eating like a boy named " + name + ".", {});
    }
};

// Example of a girl
class Girl : public HumanBeing {
public:
    std::string name;
    int age;

public:
    // If you have a constructor that takes parameters, make sure to define 
    // the default constructor as well to avoid issues.
    Girl() : name("Unknown"), age(0) {}
    Girl(std::string name, int age): name(name), age(age) {}
    

    ~Girl() {}
   
    void speak() const override {
        LOG_CONTEXT(LogLevel::DISPLAY, "Hello, I am a girl named: " + name +  " I am " + to_string(age) + " years old.", {});
    }

    void walk() const override {
        LOG_CONTEXT(LogLevel::DISPLAY, "I am walking like a girl named " + name + ".", {});
    }

    void eat() const override {
        LOG_CONTEXT(LogLevel::DISPLAY, "I am eating like a girl named " + name + ".", {});
    }
};

int main() {
    ItemManager manager;

    // Add items using constructor
    auto Item1 = std::make_shared<Boy>("Tom", 12);
    auto Item2 = std::make_shared<Girl>("Alice", 10);
    manager.addItem(Item1, "Boy1");
    manager.addItem(Item2, "Girl1");
    LOG_CONTEXT(LogLevel::INFO, "Exporting all items to XML file.", {});
    manager.getItem<Girl>("Girl1")->speak();
   
    manager.exportToFile_CSV("items.csv");
    std::cout << "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\n";
    LOG_CONTEXT(LogLevel::INFO, "Importing all items from CSV file.", {});

    /* 
      The imports of each item are validated by ID โ€” either self-defined or auto-generated.
      Hard-coded items that are not properly registered in the system will be discarded during import.
      Only items with valid registration and metadata are eligible for deserialization.
    */
    manager.importFromFile_CSV("items.csv");
    std::cout << "::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\n";
    LOG_CONTEXT(LogLevel::INFO, "Importing single object from CSV file.", {});

    /*
        You might be wondering why `typeid` is used here.
        The `typeid` operator retrieves type information at runtime, which is critical for dynamic type checking.
        During single-object import, this ensures that the correct class type is used for deserialization.

        Each exported item is tagged with its compiler-generated type name.
        This name is used to match the correct type during import.
        This approach is especially important for complex types like `std::string`, `std::vector`, and other STL containers,
        where relying on mangled names or manual identifiers can lead to deserialization errors.

        By using `typeid`, Smart_Store guarantees type-safe imports โ€” even if class names change or evolve over time.
    */
    manager.importSingleObject_CSV("items.csv", typeid(Boy).name(), "Boy1");
    return 0;
}

Output for CSV export:

output for CSV export

Output for CSV imports:

output for CSV imports

Output for CSV single import:

output for CSV single import

๐Ÿ›๏ธ Smart_Store

::| Development Guide |::

If you encounter any issues and bugs, please reach out via email or GitHub issues.

Clone this wiki locally