// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
/**
* @file xmlserializable.h
* @ingroup util
*
* @brief Interfaces to be used for XML serialization.
*
* This file contains XML utility classes, to make XML serialization simple.
* Rather than users writing their own XML, these tools let you define a tree
* structure, and this library builds the XML for you. This ensures that you
* write legal XML and that stuff that needs to be escaped is.
*
* It defines a superclass for XML serializable classes, called XmlSerializable.
* This is what classes that should be XML serializable will inherit.
*
* When implementing the printXml() function in XmlSerializable, one will
* use the various XML helper classes defined here to build a tree structure
* creating the XML. These are: XmlTag, XmlEndTag, XmlAttribute, and XmlContent.
* Some subclasses exist of XmlContent to facilitate various types of content.
*
* The XmlOutputStream wraps a regular std::ostream. You write XML objects to it
* and it is responsible for writing all the XML code. This way, the XML
* serialization is done without interfering with regular output operators.
*
* For example usage, refer to the unit test:
* vespalib/tests/xmlserializable/xmlserializabletest.cpp
*
*/
#pragma once
#include "xmlserializable.h"
#include
#include
#include
namespace vespalib::xml {
class XmlAttribute;
class XmlContent;
class XmlOutputStream;
bool isLegalName(const std::string& name);
enum class XmlTagFlags { NONE = 0, CONVERT_ILLEGAL_CHARACTERS = 1 };
/**
* @class document::XmlTag
*
* @brief Start a new tag with given name.
*/
class XmlTag {
std::string _name;
std::unique_ptr _attributes;
std::unique_ptr _content;
XmlTagFlags _flags;
public:
XmlTag(const XmlTag&);
XmlTag(const std::string& name, XmlTagFlags = XmlTagFlags::NONE);
~XmlTag();
const std::string& getName() const { return _name; }
};
/**
* @class document::XmlEndTag
*
* @brief Indicates that current tag is closed.
*/
class XmlEndTag {
public:
XmlEndTag();
};
/**
* @class document::XmlAttribute
*
* @brief Defined a single attribute within an XML tag.
*
* When adding an XML to an XML stream, the attribute will be added to the last
* tag added. This can not be called after the last tag opened in the stream is
* closed, so add all attributes before starting to add new XML child tags.
*/
class XmlAttribute {
std::string _name;
std::string _value;
std::unique_ptr _next;
public:
enum Flag { NONE = 0x0, HEX = 0x1 };
XmlAttribute(const XmlAttribute&);
/** Add any value that can be written to an ostringstream. */
template
XmlAttribute(const std::string& name, T value, uint32_t flags = NONE);
XmlAttribute(const std::string& name, const char * value, uint32_t flags = NONE);
~XmlAttribute();
const std::string& getName() const { return _name; }
const std::string& getValue() const { return _value; }
};
/**
* @class document::XmlContent
*
* XML content to be written to stream. By default it will autodetect whether to
* escape or base64 encode content. XmlOutputStream functions taking primitives
* will generate XmlContent instances.
*/
class XmlContent {
public:
enum Type { AUTO, ESCAPED, BASE64 };
protected:
XmlContent(Type type);
private:
Type _type;
std::string _content;
std::unique_ptr _nextContent;
std::unique_ptr _nextTag;
public:
XmlContent();
XmlContent(const XmlContent&);
XmlContent(const std::string& value);
~XmlContent();
Type getType() const { return _type; }
const std::string& getContent() const { return _content; }
};
/**
* @class document::XmlEscapedContent
*
* Token used to tell that this content field should only be XML escaped.
*/
class XmlEscapedContent : public XmlContent {
public:
XmlEscapedContent() : XmlContent(ESCAPED) {}
};
/**
* @class document::XmlBase64Content
*
* Token used to tell that this content field should always be base64 encoded.
*/
class XmlBase64Content : public XmlContent {
public:
XmlBase64Content() : XmlContent(BASE64) {}
};
/**
* @class document::XmlContentWrapper
*
* A wrapper class for content that one doesn't want to copy or release
* ownership of. This wrapper merely takes pointer to data, and assumes it
* will stay alive as long as needed.
*/
class XmlContentWrapper : public XmlContent {
public:
XmlContentWrapper(const XmlContentWrapper&);
XmlContentWrapper(const char* value);
XmlContentWrapper(const char* value, uint32_t size);
};
/**
* @class document::XmlOutputStream
*
* @brief std::ostream wrapper, only accepting data that will become XML.
*
* After XmlEndTag() has been sent to the stream, the tag is guarantueed to have
* been written. Call isFinalized() to ensure that you have closed all the tags
* that have been opened. Within a tag, the stream will cache some information,
* as more information might be required before knowing what to print.
*/
class XmlOutputStream {
const std::string _indent;
std::ostream& _wrappedStream;
std::list _tagStack;
std::unique_ptr _cachedTag;
std::list _cachedAttributes;
std::list _cachedContent;
XmlContent::Type _cachedContentType;
void flush(bool endTag);
public:
XmlOutputStream(std::ostream& ostream, const std::string& indent = "");
~XmlOutputStream();
bool isFinalized() const
{ return (_tagStack.empty() && _cachedTag.get() == 0); }
std::ostream& getWrappedStream() { return _wrappedStream; }
XmlOutputStream& operator<<(const XmlTag& tag);
XmlOutputStream& operator<<(const XmlAttribute& attribute);
XmlOutputStream& operator<<(const XmlEndTag& endtag);
XmlOutputStream& operator<<(const XmlContent& content);
XmlOutputStream& operator<<(const XmlSerializable& serializable);
XmlOutputStream& operator<<(const std::string& content);
XmlOutputStream& operator<<(char c);
XmlOutputStream& operator<<(int32_t i);
XmlOutputStream& operator<<(int64_t i);
XmlOutputStream& operator<<(float f);
XmlOutputStream& operator<<(double d);
};
}