Search code examples
c++iostreamostream

Stream Manipulation for outputting object data in different formats


Say I have an employee object with the following data members:

class Employee {

private:
    int _id;
    std::string _name;
    std::string _address;
    std::string _city;
    std::string _state;
    std::string _country;
    std::string _phone;
    double _salary;
...
}

I would like to output it two different ways:

XML

<Employee>
     <id>12345</id>
     <name>Jack Dough</name>
     <address>24437 Princeton</address>
     <city>Dearborn</city>
     <state>Michigan</state>
     <country>USA</country>
     <phone>303-427-0153</phone>
     <salary>140000</salary>
</Employee>

and JSON-like:

id: 12345
name: Jack Dough
address: 24437 Princeton
city: Dearborn
state: Michigan
country: USA
phone: 303-427-0153
salary: 140000

How would I be able to do so with stream manipulators? For Example:

Employee* employee = new Employee(12345, "Jack Dough", "24437 Princeton", "Dearborn", "Michigan", "USA", "303-427-0153", 140000.00);
cout << toXML << employee;
cout << toJSON << employee;

Solution

  • First of all, unless you really need to implement this as a separate manipulator, consider other routes. Two obvious possibilities would be a custom locale, or just a function that does the formatting and returns the result as a string. The former would look something like:

    std::locale myLoc(std::locale(), XML_formatter);
    cout.imbue(myLoc);
    
    cout << employee;
    

    This makes the formatting style persistent for the stream in general. If you really need to mix different styles in the same stream, the function version is a lot simpler:

    std::string toXML(Employee const &e) { 
        std::stringstream ret;
    
        ret << "Employee>\n<id>" << id << "</id>"
            << // ...
        return ret.str();
    }
    
    // ...
    cout << toXML(employees[i]);
    

    If you truly have no choice but to implement this as a separate manipulator, you'll need to store a flag to indicate the current format in the stream. Streams provide a memory management interface in the form of xalloc, iword and pword. Basically, xalloc allocates one word for you. iword gives you access to that word as a reference to a long, and pword gives you access to it as a reference to pointer to void. In your case, you apparently only need one or two bits, so you probably want to use iword and define a couple of bits to specify the formatting for the type. Your toXML and toJSON manipulators will set the appropriate bits, and your operator<< will read them to control its behavior. It's clumsy and ugly, but it does work if you're willing to put a little effort into it.