Giter Club home page Giter Club logo

xmlwriter's Introduction

Arduino CI Arduino-lint JSON check GitHub issues

License: MIT GitHub release PlatformIO Registry

XMLWriter

Arduino Library to create simple XML (messages, files, print, ...).

Description

The XMLWriter class supports generating XML files and send these over a stream like Ethernet SD.File or Serial.

When instantiating an XMLWriter one can define the internal buffer size. A bigger buffer will make the output faster, especially for Ethernet and SD.File. The buffer size should be at least 2 bytes and max 250. How much faster depends on the properties of the stream and the platform used. E.g. the baud rate and internal buffer of Serial, packet behaviour of Ethernet, or paging of SD cards. If performance is low one should do test runs with different sizes for the buffer and choose one that is appropriate.

Indicative sizes based upon the examples. Run your tests to find your application optimum.

STREAM SIZE
Ethernet 20-30
Serial 5
SD File 10-16

IMPORTANT: When using buffering you should always call XML.flush() at the end of the XML generation. This will flush the last bytes in the internal buffer into the output stream.

Interface

#include "XMLWriter.h"

Constructor

  • XMLWriter(Print* stream = &Serial, uint8_t bufferSize = 10) Constructor defines the stream and the buffer size to optimize performance vs memory usage. Note the default bufferSize of 10 can be optimized. See table in description above.

Functions for manual layout control

  • void setIndentSize(uint8_t size = 2) preferred a multiple of 2; no limit.
  • uint8_t getIndentSize() returns set indent.
  • void incrIndent() increments indent by 2 spaces.
  • void decrIndent() decrements indent by 2 spaces.
  • void indent() manually indent output.
  • void raw(char* str) inject any string.

General settings

  • void setConfig(uint8_t config) used to show/strip comment, indent, newLine. use setConfig(0); to minimize the output.
  • void newLine(uint8_t n) add a number of newlines to the output, default = 1.

Functions

  • void header() injects standard XML header string, must be first line.
  • void reset() resets internal state, to be called before new XML is written.
  • void comment(char* text, bool multiLine = false) <!-- text -->
    if multiline == true it does not indent to allow bigger text blocks multiline is default false.
  • void flush() call flush() at the end of writing to empty the internal buffer. !!

Functions to create simple tags with named fields

  • void tagOpen(char* tag, bool newLine) <tag>
  • void tagOpen(char* tag, char* name, bool newLine) <tag name="name">
  • void tagClose() </tag>

Functions to make up tags with multiple fields

  • void tagStart(char* tag) <tag
  • void tagField(char* field, char* string) field="string"
  • void tagField(char* field, T value, uint8_t base = DEC) standard math types, field="value"
  • void tagEnd(bool newline = true, bool addSlash = true); />

Functions to make a node

  • void writeNode(char* tag, bool value); <tag>value</tag>
  • void writeNode(char* tag, T value, uint8_t base = DEC); standard math types.

Helper

  • void escape(char* str) expands the XML chars: "'<>& Note one need to set the XMLWRITER_ESCAPE_SUPPORT flag.

Metrics and debug

To optimize buffer size in combination with timing.

  • uint8_t bufferIndex() returns the size of the internal buffer.
  • uint32_t bytesWritten() idem, since reset().
  • void version() injects the XMLWRITER_VERSION as comment in output stream.
  • void debug() injects comment with internal info.

Print interface

XMLWriter 0.2.4 implements the Print interface, so at any moment one can use print() or println() to inject specific information.

Note that tagField() and writeNode() do not support 64 bit integer types and large values of double. My printHelpers library helps to convert these to strings which can be printed. See example.

The Print interface can also be used to print objects that implement the Printable interface. See example.

With the support of the Print interface, raw() is becoming obsolete as it only can inject strings.

Configuration flags

Flag Value Description
XMLWRITER_NONE 0x00 minimize output, smaller & faster
XMLWRITER_COMMENT 0x01 allow comments
XMLWRITER_INDENT 0x02 allow indentation
XMLWRITER_NEWLINE 0x04 allow newlines
  • setConfig(XMLWRITER_NONE); to minimize the output in bytes.
  • setConfig(XMLWRITER_NEWLINE); to break an XML stream in lines.
  • setConfig(XMLWRITER_NEWLINE | XMLWRITER_INDENT); to see XML structure.
  • setConfig(XMLWRITER_NEWLINE | XMLWRITER_INDENT | XMLWRITER_COMMENT); to see XML structure + comments.

Operation

See examples

Future

Must

  • update documentation

Should

Could

  • move code to .cpp

Wont

Support

If you appreciate my libraries, you can support the development and maintenance. Improve the quality of the libraries by providing issues and Pull Requests, or donate through PayPal or GitHub sponsors.

Thank you,

xmlwriter's People

Contributors

nickels avatar robtillaart avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

nickels

xmlwriter's Issues

add examples

  • use XML.print() or XML.println() use of Print interface
  • use of printHelpers with scientific notation and 64 bit int.
  • print interface should be public

something like below but more elaborated examples

XML.tagOpen(Customer, name);
XML.print(address_information);
XML.tagClose(true);

XML.tagOpen(Currency, "dollars");
XML.print(print64(amount));
XML.tagClose(true);

XML.tagOpen(Distance, "sun");
XML.print(sci((AU, 8));
XML.tagClose(true);

Save to String

I want to congratulate the developer for the outstanding work on this library.

I am trying to use this library to create an XML and save it into a String. Is this possible with your library?
If so, please can you provide some guidance?

Thank you

Closing tag not printed in full.

Hi Rob, first of all thank you again for this great lib! I've been using it for a while now and since the latest release it has been acting up of me.

I have the below switch case in an endless loop which uses values from a queue in FreeRTOS.

            switch (XML_VALUES_RECEIVED.ACTION_TYPE)
            {
            case HEADER: // 1 HEADER
                header(TCX, XML_VALUES_RECEIVED.unixTimestamp);
                break;
            case OPEN_LAP: // 2 OPEN LAP
                openLap(TCX, XML_VALUES_RECEIVED.unixTimestamp);
                break;
            case TRACKPOINT: // 3 TRACKPOINT
                trackPoint(TCX, XML_VALUES_RECEIVED.unixTimestamp, XML_VALUES_RECEIVED.distanceInMeters);
                break;
            case CLOSE_LAP: // 4 CLOSE LAP
                closeLap(TCX, XML_VALUES_RECEIVED.durationInSeconds, XML_VALUES_RECEIVED.distanceInMeters);
                break;
            case FOOTER: // 5 FOOTER
                footer(TCX);
                tcxFile.close();
            }

The methods called in the above switch case below.

void header(XMLWriter &xmlInstance, time_t timestamp)
{
    serial_println("[XML] Writing header.");
    xmlInstance.header();
    xmlInstance.tagStart("TrainingCenterDatabase");
    xmlInstance.tagField("xsi:schemaLocation", "http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd");
    xmlInstance.tagField("xmlns:ns5", "http://www.garmin.com/xmlschemas/ActivityGoals/v1");
    xmlInstance.tagField("xmlns:ns3", "http://www.garmin.com/xmlschemas/ActivityExtension/v2");
    xmlInstance.tagField("xmlns:ns2", "http://www.garmin.com/xmlschemas/UserProfile/v2");
    xmlInstance.tagField("xmlns", "http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2");
    xmlInstance.tagField("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
    xmlInstance.tagField("xmlns:ns4", "http://www.garmin.com/xmlschemas/ProfileExtension/v1");
    xmlInstance.tagEnd(NEWLINE, NOSLASH);

    xmlInstance.tagOpen("Activities");
    xmlInstance.tagStart("Activity");
    xmlInstance.tagField("Sport", "Other");
    xmlInstance.tagEnd(NEWLINE, NOSLASH);

    xmlInstance.writeNode("Id", tcxTimestamp(timestamp));
}

void openLap(XMLWriter &xmlInstance, time_t timestamp)
{
    serial_println("[XML] Opening lap.");
    xmlInstance.tagStart("Lap");
    xmlInstance.tagField("StartTime", tcxTimestamp(timestamp)); //<Lap StartTime="2019-08-15T16:04:16.000Z">
    xmlInstance.tagEnd(NEWLINE, NOSLASH);
    xmlInstance.incrIndent();

    xmlInstance.writeNode("Intensity", "Active");     //<Intensity>Active</Intensity>
    xmlInstance.writeNode("TriggerMethod", "Manual"); //<TriggerMethod>Manual</TriggerMethod>
    xmlInstance.tagOpen("Track");                     //<Track>
}

void trackPoint(XMLWriter &xmlInstance, time_t timestamp, float distance)
{
    serial_println("[XML] Writing trackpoint.");
    xmlInstance.tagOpen("Trackpoint");                      //<Trackpoint>
    xmlInstance.writeNode("Time", tcxTimestamp(timestamp)); //<Time>2019-08-15T16:04:16.000Z</Time>
    xmlInstance.writeNode("DistanceMeters", distance);      //<DistanceMeters>0.0</DistanceMeters>
    xmlInstance.tagClose();                                 //</Trackpoint>
}

void closeLap(XMLWriter &xmlInstance, time_t total_time_in_sec, float distance)
{
    serial_println("[XML] Closing lap.");
    xmlInstance.tagClose();                                                 //Track
    xmlInstance.writeNode("TotalTimeSeconds", (uint16_t)total_time_in_sec); //<TotalTimeSeconds>5</TotalTimeSeconds>
    xmlInstance.writeNode("DistanceMeters", distance);                      //<DistanceMeters>0.0</DistanceMeters>

    xmlInstance.tagStart("/Lap"); //Lap
    xmlInstance.tagEnd(NEWLINE, NOSLASH);
}

void footer(XMLWriter &xmlInstance)
{
    serial_println("[XML] Writing footer.");
    xmlInstance.tagStart("/Activity"); //Activity
    xmlInstance.tagEnd(NEWLINE, NOSLASH);

    xmlInstance.tagClose();                          //Activities
    xmlInstance.tagStart("/TrainingCenterDatabase"); //Activity
    xmlInstance.tagEnd(NEWLINE, NOSLASH);
}

As I'm using PlatformIO it is possible to redefine definitions of the XMLWriter package, hence PR 7e2966a . The longest tag is 27 characters so I've set the length to 30 as per below.

build_flags =
  ;Max tag length for XML writer
  -D XMLWRITER_MAXTAGSIZE=30

Now the XML file is created and written to an SD card, that's all good. However it's not valid as the latest tag looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<TrainingCenterDatabase xsi:schemaLocation="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd" xmlns:ns5="http://www.garmin.com/xmlschemas/ActivityGoals/v1" xmlns:ns3="http://www.garmin.com/xmlschemas/ActivityExtension/v2" xmlns:ns2="http://www.garmin.com/xmlschemas/UserProfile/v2" xmlns="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns4="http://www.garmin.com/xmlschemas/ProfileExtension/v1">
	<Activities>
	  <Activity Sport="Other">
	  <Id>2020-05-24T15:53:07.000000Z</Id>
	  <Lap StartTime="2020-05-24T15:53:07.000000Z">
	    <Intensity>Active</Intensity>
	    <TriggerMethod>Manual</TriggerMethod>
	    <Track>
	      <Trackpoint>
	        <Time>2020-05-24T15:53:07.000000Z</Time>
	        <DistanceMeters>0.00</DistanceMeters>
	      </Trackpoint>
	      <Trackpoint>
	        <Time>2020-05-24T15:53:08.000000Z</Time>
	        <DistanceMeters>0.72</DistanceMeters>
	      </Trackpoint>
	      <Trackpoint>
	        <Time>2020-05-24T15:53:10.000000Z</Time>
	        <DistanceMeters>1.47</DistanceMeters>
	      </Trackpoint>
	      <Trackpoint>
	        <Time>2020-05-24T15:53:11.000000Z</Time>
	        <DistanceMeters>2.24</DistanceMeters>
	      </Trackpoint>
	      <Trackpoint>
	        <Time>2020-05-24T15:55:11.000000Z</Time>
	        <DistanceMeters>97.48</DistanceMeters>
	      </Trackpoint>
	    </Track>
	    <TotalTimeSeconds>124</TotalTimeSeconds>
	    <DistanceMeters>97.48</DistanceMeters>
	    </Lap>
	  </Activity>
	</Activities>
</TrainingCenterDa

instead of:

<?xml version="1.0" encoding="UTF-8"?>
<TrainingCenterDatabase xsi:schemaLocation="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd" xmlns:ns5="http://www.garmin.com/xmlschemas/ActivityGoals/v1" xmlns:ns3="http://www.garmin.com/xmlschemas/ActivityExtension/v2" xmlns:ns2="http://www.garmin.com/xmlschemas/UserProfile/v2" xmlns="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns4="http://www.garmin.com/xmlschemas/ProfileExtension/v1">
	<Activities>
	  <Activity Sport="Other">
	  <Id>2020-05-24T15:53:07.000000Z</Id>
	  <Lap StartTime="2020-05-24T15:53:07.000000Z">
	    <Intensity>Active</Intensity>
	    <TriggerMethod>Manual</TriggerMethod>
	    <Track>
	      <Trackpoint>
	        <Time>2020-05-24T15:53:07.000000Z</Time>
	        <DistanceMeters>0.00</DistanceMeters>
	      </Trackpoint>
	      <Trackpoint>
	        <Time>2020-05-24T15:53:08.000000Z</Time>
	        <DistanceMeters>0.72</DistanceMeters>
	      </Trackpoint>
	      <Trackpoint>
	        <Time>2020-05-24T15:53:10.000000Z</Time>
	        <DistanceMeters>1.47</DistanceMeters>
	      </Trackpoint>
	      <Trackpoint>
	        <Time>2020-05-24T15:53:11.000000Z</Time>
	        <DistanceMeters>2.24</DistanceMeters>
	      </Trackpoint>
	      <Trackpoint>
	        <Time>2020-05-24T15:55:11.000000Z</Time>
	        <DistanceMeters>97.48</DistanceMeters>
	      </Trackpoint>
	    </Track>
	    <TotalTimeSeconds>124</TotalTimeSeconds>
	    <DistanceMeters>97.48</DistanceMeters>
	    </Lap>
	  </Activity>
	</Activities>
</TrainingCenterDatabase>

Any thoughts?

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.