## Input and output

The class `zeep::xml::document` derives from `zeep::xml::element` can load from and write to files.

#### streaming I/O

You can use std::iostream to read and write `zeep::xml::document` objects. Reading is as simple as:

    zeep::xml::document doc;
    std::cin >> doc;

Writing is just as simple. A warning though, round trip fidelity is not guaranteed. There are a few issues with that. First of all, the default is to replace CDATA sections in a file with their content. If this is not the desired behaviour you can call `set_preserve_cdata(true)`.

Another issue is that text nodes containing only white space are present in documents read from disk while these are absent by default in documents created on the fly. When writing out XML using `iostream` you can specify to wrap and indent a document. But if the document was read in, the result will have extraneous spacing.

Specifying indentation is BTW done like this:

    std::cout << std::setw(2) << doc;

That will indent with two spaces for each level.

#### validation

This will not validate the XML using the DTD by default. If you do want to validate and process the DTD, you have to specify where to find this DTD and other external entities. You can either use `set_base_dir` or you can specify an entity_loader using `set_entity_loader`

As an example, take the following DTD file

    <!ELEMENT foo (bar)>
    <!ELEMENT bar (#PCDATA)>
    <!ENTITY hello "Hello, world!">

And an XML document containing

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE foo SYSTEM "sample.dtd">
    <foo>
    	<bar>&hello;</bar>
    </foo>

When we want to see the `&hello;` entity replaced with `'Hello, world!'` as specified in the DTD, we need to provide a way to load this DTD. To do this, look at the following code. Of course, in this example a simple call to `set_base_dir` would have been sufficient.

    #include <filesystem>
    #include <fstream>

    #include <zeep/xml/document.hpp>

    namespace fs = std::filesystem;

    int main()
    {
        auto loader = []
            (const std::string& base, const std::string& pubid, const std::string& sysid) -> std::istream*
        {
            if (base == "." and pubid.empty() and fs::exists(sysid))
                return new std::ifstream(sysid);

            throw std::invalid_argument("Invalid arguments passed in loader");
        };

        zeep::xml::document doc;
        doc.set_entity_loader(loader);

        std::ifstream is("sample.xml");
        is >> doc;

        using namespace zeep::xml::literals;

        if (doc == R"(<foo><bar>Hello, world!</bar></foo>)"_xml)
            std::cout << "ok" << std::endl;

        return 0;
    }

| Define an entity loader function
| Create document and set the entity loader
| Read a file
| Compare the doc with an in-memory constructed document, note that spaces are ignored