From: Graeme Foot <Graeme.Foot@touchcut.com>
Date: Thu Nov 04 15:03:32 2021 +1300

adding ability to output json or xml for ethercat commands:
* graph
* slaves

for json output specify option --json or -j
for xml output specify option --xml or -x

if neither are specified the standard output is used

diff --git a/tool/Command.cpp b/tool/Command.cpp
--- a/tool/Command.cpp
+++ b/tool/Command.cpp
@@ -30,7 +30,10 @@
  ****************************************************************************/
 
 #include <map>
+#include <sstream>
 #include <iostream>
+#include <string>
+#include <iomanip>
 using namespace std;
 
 #include "Command.h"
@@ -151,6 +154,7 @@
 Command::Command(const string &name, const string &briefDesc):
     name(name),
     briefDesc(briefDesc),
+    outputType(stdOutput),
     verbosity(Normal),
     emergency(false),
     force(false),
@@ -173,6 +177,13 @@
 
 /*****************************************************************************/
 
+void Command::setOutputType(OutputType ot)
+{
+    outputType = ot;
+};
+
+/*****************************************************************************/
+
 void Command::setVerbosity(Verbosity v)
 {
     verbosity = v;
@@ -548,3 +559,54 @@
 }
 
 /****************************************************************************/
+
+string Command::escapeJson(const string &s)
+{
+    ostringstream oss;
+
+    // escape standard escape characters and then escape the rest of the
+    // special characters with character codes
+    for (string::const_iterator c = s.begin(); c != s.end(); c++) {
+        switch (*c) {
+            case '"'  : oss << "\\\""; break;
+            case '\\' : oss << "\\\\"; break;
+            case '\b' : oss << "\\b"; break;
+            case '\f' : oss << "\\f"; break;
+            case '\n' : oss << "\\n"; break;
+            case '\r' : oss << "\\r"; break;
+            case '\t' : oss << "\\t"; break;
+            default : {
+                if ( ('\x00' <= *c) && (*c <= '\x1f') ) {
+                    oss << "\\u" << hex << setw(4) << setfill('0') << (int)*c;
+                } else {
+                    oss << *c;
+                }
+            }
+        }
+    }
+    
+    return oss.str();
+}
+
+/****************************************************************************/
+
+string Command::escapeXml(const string &s)
+{
+    ostringstream oss;
+
+    // escape special xml characters
+    for (string::const_iterator c = s.begin(); c != s.end(); c++) {
+        switch (*c) {
+            case '&'  : oss << "&amp;"; break;
+            case '\"' : oss << "&quot;"; break;
+            case '\'' : oss << "&apos;"; break;
+            case '<'  : oss << "&lt;"; break;
+            case '>'  : oss << "&gt;"; break;
+            default   : oss << *c;
+        }
+    }
+    
+    return oss.str();
+}
+
+/****************************************************************************/
diff --git a/tool/Command.h b/tool/Command.h
--- a/tool/Command.h
+++ b/tool/Command.h
@@ -89,6 +89,14 @@
         MasterIndexList getMasterIndices() const;
         unsigned int getSingleMasterIndex() const;
 
+        enum OutputType {
+            stdOutput,
+            jsonOutput,
+            xmlOutput
+        };
+        void setOutputType(OutputType);
+        OutputType getOutputType() const;
+
         enum Verbosity {
             Quiet,
             Normal,
@@ -150,6 +158,8 @@
         int emergencySlave() const;
 
         static string alStateString(uint8_t);
+        static string escapeJson(const string &);
+        static string escapeXml(const string &);
 
         string aliases;
         string positions;
@@ -158,6 +168,7 @@
         string name;
         string briefDesc;
         string masters;
+        OutputType outputType;
         Verbosity verbosity;
         string domains;
         string dataType;
@@ -186,6 +197,13 @@
 
 /****************************************************************************/
 
+inline Command::OutputType Command::getOutputType() const
+{
+    return outputType;
+}
+
+/****************************************************************************/
+
 inline Command::Verbosity Command::getVerbosity() const
 {
     return verbosity;
diff --git a/tool/CommandGraph.cpp b/tool/CommandGraph.cpp
--- a/tool/CommandGraph.cpp
+++ b/tool/CommandGraph.cpp
@@ -28,6 +28,7 @@
  ****************************************************************************/
 
 #include <iostream>
+#include <iomanip>
 #include <map>
 #include <algorithm>
 using namespace std;
@@ -54,7 +55,12 @@
         << endl
         << getBriefDescription() << endl
         << endl
-        << "The bus is output in DOT language (see" << endl
+        << "The output language can be one of the following:" << endl
+        << "  DOT  (default)" << endl
+        << "  JSON (--json, -j)" << endl
+        << "  XML  (--xml, -x)" << endl
+        << endl
+        << "If the bus is output in DOT language (default, see" << endl
         << "http://www.graphviz.org/doc/info/lang.html), which can" << endl
         << "be processed with the tools from the Graphviz" << endl
         << "package. Example:" << endl
@@ -91,6 +97,7 @@
 
 void CommandGraph::execute(const StringVector &args)
 {
+    Command::OutputType outputType;
     Info info = None;
     ec_ioctl_master_t master;
     typedef vector<ec_ioctl_slave_t> SlaveVector;
@@ -98,6 +105,7 @@
     typedef vector<CrcInfo> CrcInfoVector;
     CrcInfoVector crcInfos;
     ec_ioctl_slave_t slave;
+    int masterIdx;
     SlaveVector::const_iterator si;
     map<int, string> portMedia;
     map<int, string>::const_iterator mi;
@@ -133,7 +141,8 @@
         }
     }
 
-    MasterDevice m(getSingleMasterIndex());
+    masterIdx = getSingleMasterIndex();
+    MasterDevice m(masterIdx);
     m.open(MasterDevice::Read);
     m.getMaster(&master);
 
@@ -169,122 +178,403 @@
             crcInfos.push_back(crcInfo);
         }
     }
+    
 
-    cout << "/* EtherCAT bus graph. Generated by 'ethercat graph'. */" << endl
-        << endl
-        << "strict graph bus {" << endl
-        << "    rankdir=\"LR\"" << endl
-        << "    ranksep=0.8" << endl
-        << "    nodesep=0.8" << endl
-        << "    node [fontname=\"Helvetica\"]" << endl
-        << "    edge [fontname=\"Helvetica\",fontsize=\"10\"]" << endl
-        << endl
-        << "    master [label=\"EtherCAT\\nMaster\"]" << endl;
+    outputType = getOutputType();
+    switch (outputType) {
+        case Command::jsonOutput : {
+            cout << "{" << endl
+                << "  \"bus\": {" << endl
+                << "    \"master\": {" << endl
+                << "      \"index\": " << masterIdx << "," << endl
+                << "      \"slaves\": [";
+
+            uint16_t alias = 0x0000;
+            uint16_t pos = 0;
+
+            for (si = slaves.begin(); si != slaves.end(); si++) {
+                if (si->alias) {
+                    alias = si->alias;
+                    pos = 0;
+                }
+
+                if (si == slaves.begin()) {
+                    cout << endl;
+                } else {
+                    cout << "," << endl;
+                }
+
+                cout << "        {" << endl
+                    << "          \"index\": "
+                    << si->position << "," << endl
+                    << "          \"alias\": "
+                    << alias << "," << endl
+                    << "          \"position\": "
+                    << pos << "," << endl
+                    << "          \"vendor_id\": "
+                    << dec << si->vendor_id << "," << endl
+                    << "          \"product_code\": "
+                    << dec << si->product_code << "," << endl
+                    << "          \"revision_number\": "
+                    << dec << si->revision_number << "," << endl
+                    << "          \"serial_number\": "
+                    << dec << si->serial_number << "," << endl;
+
+                // device order number
+                if (string(si->order).size()) {
+                    cout << "          \"order_number\": \""
+                        << escapeJson(si->order) << "\"," << endl;
+                }
+
+                // distributed clock info
+                if (info == DC && si->dc_supported) {
+                    cout << "          \"dc\": {" << endl;
+                    if (si->has_dc_system_time) {
+                        switch (si->dc_range) {
+                            case EC_DC_32:
+                                cout << "            \"range\": \"32 bit\","
+                                    << endl;
+                                break;
+                            case EC_DC_64:
+                                cout << "            \"range\": \"64 bit\","
+                                    << endl;
+                                break;
+                            default:
+                                cout << "            \"range\": \"\","
+                                    << endl;
+                                break;
+                        }
+                    } else {
+                        cout << "            \"range\": \"Delay meas.\","
+                            << endl;
+                    }
+                    cout << "            \"delay\": \""
+                        << si->transmission_delay << " ns\"" << endl
+                        <<  "          }," << endl;
+                }
+
+                // port info
+                cout << "          \"ports\": [";
+                int hasPort = 0;
+                for (int port = 0; port < EC_MAX_PORTS; port++) {
+                    uint16_t next_pos = si->ports[port].next_slave;
 
-    if (slaves.size()) {
-        cout << "    master -- slave0";
-        mi = portMedia.find(slaves.front().ports[0].desc);
-        if (mi != portMedia.end())
-            cout << "[label=\"" << mi->second << "\"]";
+                    if (next_pos == 0xffff) {
+                        continue;
+                    }
+
+                    if (next_pos >= slaves.size()) {
+                        cerr << "Invalid next slave pointer." << endl;
+                        continue;
+                    }
+                    
+                    if (hasPort) {
+                        cout << "," << endl;
+                    } else {
+                        cout << endl;
+                        hasPort = 1;
+                    }
+
+                    cout << "            {" << endl
+                        << "              \"port\": " << port << "," << endl
+                        << "              \"next_slave\": " << next_pos << ","
+                        << endl;
+
+                    if (info == DC && si->dc_supported) {
+                        cout << "              \"delay_to_next_dc\": "
+                            << si->ports[port].delay_to_next_dc << ","
+                            << endl;
+                    }
+                    if (info == CRC) {
+                        CrcInfo *crcInfo = &crcInfos[si->position];
+                        cout << "              \"crc\": "
+                            << crcInfo->crc[port] << "," << endl
+                            << "              \"phy\": "
+                            << crcInfo->phy[port] << "," << endl
+                            << "              \"fwd\": "
+                            << crcInfo->fwd[port] << "," << endl
+                            << "              \"lnk\": "
+                            << crcInfo->lnk[port] << "," << endl;
+                    }
 
-        cout << endl;
-    }
-    cout << endl;
+                    ec_ioctl_slave_t *next = &slaves[next_pos];
+                    mi = portMedia.find(si->ports[port].desc);
+                    if (mi == portMedia.end() && next) {
+                        /* Try medium of next-hop slave. */
+                        mi = portMedia.find(next->ports[0].desc);
+                    }
+
+                    if (mi != portMedia.end()) {
+                        cout << "              \"media\": \"" << mi->second
+                            << "\"" << endl;
+                    } else {
+                        cout << "              \"media\": \"Unknown\""
+                            << endl;
+                    }
 
-    uint16_t alias = 0x0000;
-    uint16_t pos = 0;
+                    cout << "            }";
+                }
+                cout << endl 
+                    << "          ]" << endl;
+                
+                // end of slave
+                cout << "        }";
+                
+                pos++;
+            }
+
+            cout << endl
+                << "      ]" << endl
+                << "    }" << endl
+                << "  }" << endl
+                << "}" << endl;
+        } break;
+    
+        case Command::xmlOutput : {
+            cout << "<?xml version=\"1.0\" ?>" << endl
+                << "<bus>" << endl
+                << "  <master>" << endl
+                << "    <index>" << masterIdx << "</index>" << endl
+                << "    <slaves>" << endl;
+
+            uint16_t alias = 0x0000;
+            uint16_t pos = 0;
 
-    for (si = slaves.begin(); si != slaves.end(); si++) {
-        if (si->alias) {
-            alias = si->alias;
-            pos = 0;
-        }
+            for (si = slaves.begin(); si != slaves.end(); si++) {
+                if (si->alias) {
+                    alias = si->alias;
+                    pos = 0;
+                }
+
+                cout << "      <slave>" << endl
+                    << "        <index>" << si->position << "</index>" << endl
+                    << "        <alias>" << alias << "</alias>" << endl
+                    << "        <position>" << pos << "</position>" << endl
+                    << "        <vendor_id>" << dec
+                    << si->vendor_id << "</vendor_id>" << endl
+                    << "        <product_code>" << dec
+                    << si->product_code << "</product_code>" << endl
+                    << "        <revision_number>" << dec
+                    << si->revision_number << "</revision_number>" << endl
+                    << "        <serial_number>" << dec
+                    << si->serial_number << "</serial_number>" << endl;
+
+                // device order number
+                if (string(si->order).size()) {
+                    cout << "        <order_number>" << escapeXml(si->order)
+                        << "</order_number>" << endl;
+                }
+
+                // distributed clock info
+                if (info == DC && si->dc_supported) {
+                    cout << "        <dc>" << endl;
+                    if (si->has_dc_system_time) {
+                        switch (si->dc_range) {
+                            case EC_DC_32:
+                                cout << "          <range>32 bit</range>"
+                                    << endl;
+                                break;
+                            case EC_DC_64:
+                                cout << "          <range>64 bit</range>"
+                                    << endl;
+                                break;
+                            default:
+                                cout << "          <range/>" << endl;
+                                break;
+                        }
+                    } else {
+                        cout << "          <range>Delay meas.</range>"
+                            << endl;
+                    }
+                    cout << "          <delay>" << si->transmission_delay
+                        << " ns</delay>" << endl
+                        <<  "        </dc>" << endl;
+                }
+
+                // port info
+                cout << "        <ports>" << endl;
+                for (int port = 0; port < EC_MAX_PORTS; port++) {
+                    uint16_t next_pos = si->ports[port].next_slave;
+
+                    if (next_pos == 0xffff) {
+                        continue;
+                    }
+
+                    if (next_pos >= slaves.size()) {
+                        cerr << "Invalid next slave pointer." << endl;
+                        continue;
+                    }
+
+                    cout << "          <port>" << endl
+                        << "            <index>" << port
+                        << "</index>" << endl
+                        << "            <next_slave>" << next_pos
+                        << "</next_slave>" << endl;
 
-        cout << "    slave" << si->position << " [shape=\"box\""
-            << ",label=\"" << si->position
-            << " / " << alias << ":" << pos;
-        if (string(si->order).size())
-            cout << "\\n" << si->order;
-        if (info == DC && si->dc_supported) {
-            cout << "\\nDC: ";
-            if (si->has_dc_system_time) {
-                switch (si->dc_range) {
-                    case EC_DC_32:
-                        cout << "32 bit";
-                        break;
-                    case EC_DC_64:
-                        cout << "64 bit";
-                        break;
-                    default:
-                        break;
+                    if (info == DC && si->dc_supported) {
+                        cout << "            <delay_to_next_dc>"
+                            << si->ports[port].delay_to_next_dc
+                            << "</delay_to_next_dc>" << endl;
+                    }
+                    if (info == CRC) {
+                        CrcInfo *crcInfo = &crcInfos[si->position];
+                        cout << "            <crc>" << crcInfo->crc[port]
+                            << "</crc>" << endl
+                            << "            <phy>" << crcInfo->phy[port]
+                            << "</phy>" << endl
+                            << "            <fwd>" << crcInfo->fwd[port]
+                            << "</fwd>" << endl
+                            << "            <lnk>" << crcInfo->lnk[port]
+                            << "</lnk>" << endl;
+                    }
+
+                    ec_ioctl_slave_t *next = &slaves[next_pos];
+                    mi = portMedia.find(si->ports[port].desc);
+                    if (mi == portMedia.end() && next) {
+                        /* Try medium of next-hop slave. */
+                        mi = portMedia.find(next->ports[0].desc);
+                    }
+
+                    if (mi != portMedia.end()) {
+                        cout << "            <media>" << mi->second
+                            << "</media>" << endl;
+                    } else {
+                        cout << "            <media>Unknown</media>" << endl;
+                    }
+
+                    cout << "          </port>" << endl;
                 }
-            } else {
-                cout << "Delay meas.";
+                cout << "        </ports>" << endl;
+
+                // close off slave
+                cout << "      </slave>" << endl;
+                pos++;
+            }
+
+            cout << "    </slaves>" << endl
+                << "  </master>" << endl
+                << "</bus>" << endl;
+        } break;
+    
+        default /*DOT*/ : {
+            cout << "/* EtherCAT bus graph. Generated by 'ethercat graph'. */"
+                << endl << endl
+                << "strict graph bus {" << endl
+                << "    rankdir=\"LR\"" << endl
+                << "    ranksep=0.8" << endl
+                << "    nodesep=0.8" << endl
+                << "    node [fontname=\"Helvetica\"]" << endl
+                << "    edge [fontname=\"Helvetica\",fontsize=\"10\"]" << endl
+                << endl
+                << "    master [label=\"EtherCAT\\nMaster\"]" << endl;
+
+            if (slaves.size()) {
+                cout << "    master -- slave0";
+                mi = portMedia.find(slaves.front().ports[0].desc);
+                if (mi != portMedia.end())
+                    cout << "[label=\"" << mi->second << "\"]";
+
+                cout << endl;
             }
-            cout << "\\nDelay: " << si->transmission_delay << " ns";
-        }
-        cout << "\"]" << endl;
+            cout << endl;
+
+            uint16_t alias = 0x0000;
+            uint16_t pos = 0;
+
+            for (si = slaves.begin(); si != slaves.end(); si++) {
+                if (si->alias) {
+                    alias = si->alias;
+                    pos = 0;
+                }
+
+                cout << "    slave" << si->position << " [shape=\"box\""
+                    << ",label=\"" << si->position
+                    << " / " << alias << ":" << pos;
+                if (string(si->order).size())
+                    cout << "\\n" << si->order;
+                if (info == DC && si->dc_supported) {
+                    cout << "\\nDC: ";
+                    if (si->has_dc_system_time) {
+                        switch (si->dc_range) {
+                            case EC_DC_32:
+                                cout << "32 bit";
+                                break;
+                            case EC_DC_64:
+                                cout << "64 bit";
+                                break;
+                            default:
+                                break;
+                        }
+                    } else {
+                        cout << "Delay meas.";
+                    }
+                    cout << "\\nDelay: " << si->transmission_delay << " ns";
+                }
+                cout << "\"]" << endl;
 
 
-        for (int port = 1; port < EC_MAX_PORTS; port++) {
-            uint16_t next_pos = si->ports[port].next_slave;
+                for (int port = 1; port < EC_MAX_PORTS; port++) {
+                    uint16_t next_pos = si->ports[port].next_slave;
+
+                    if (next_pos == 0xffff) {
+                        continue;
+                    }
+
+                    if (next_pos >= slaves.size()) {
+                        cerr << "Invalid next slave pointer." << endl;
+                        continue;
+                    }
+
+                    ec_ioctl_slave_t *next = &slaves[next_pos];
 
-            if (next_pos == 0xffff) {
-                continue;
-            }
+                    cout << "    slave" << si->position << " -- "
+                        << "slave" << next_pos << " [taillabel=\"" << port;
 
-            if (next_pos >= slaves.size()) {
-                cerr << "Invalid next slave pointer." << endl;
-                continue;
-            }
+                    if (info == DC && si->dc_supported) {
+                        cout << " [" << si->ports[port].delay_to_next_dc
+                            << "]";
+                    }
+                    if (info == CRC) {
+                        CrcInfo *crcInfo = &crcInfos[si->position];
+                        cout << " [" << crcInfo->crc[port] << "/"
+                            << crcInfo->fwd[port] << "]";
+                    }
+
+                    cout << "\",headlabel=\"0";
 
-            ec_ioctl_slave_t *next = &slaves[next_pos];
-
-            cout << "    slave" << si->position << " -- "
-                << "slave" << next_pos << " [taillabel=\"" << port;
+                    if (info == DC && next->dc_supported) {
+                        cout << " [" << next->ports[0].delay_to_next_dc << "]";
+                    }
+                    if (info == CRC) {
+                        CrcInfo *crcInfo = &crcInfos[next_pos];
+                        cout << " [" << crcInfo->crc[0] << "/"
+                            << crcInfo->fwd[0] << "]";
+                    }
+                    cout << "\"";
 
-            if (info == DC && si->dc_supported) {
-                cout << " [" << si->ports[port].delay_to_next_dc << "]";
-            }
-            if (info == CRC) {
-                CrcInfo *crcInfo = &crcInfos[si->position];
-                cout << " [" << crcInfo->crc[port] << "/"
-                    << crcInfo->fwd[port] << "]";
+                    mi = portMedia.find(si->ports[port].desc);
+                    if (mi == portMedia.end() && next) {
+                        /* Try medium of next-hop slave. */
+                        mi = portMedia.find(next->ports[0].desc);
+                    }
+
+                    if (mi != portMedia.end())
+                        cout << ",label=\"" << mi->second << "\"";
+
+                    wi = mediaWeights.find(si->ports[port].desc);
+                    if (wi != mediaWeights.end())
+                        cout << ",weight=\"" << wi->second << "\"";
+
+                    cout << "]" << endl;
+                }
+
+                cout << endl;
+                pos++;
             }
 
-            cout << "\",headlabel=\"0";
-
-            if (info == DC && next->dc_supported) {
-                cout << " [" << next->ports[0].delay_to_next_dc << "]";
-            }
-            if (info == CRC) {
-                CrcInfo *crcInfo = &crcInfos[next_pos];
-                cout << " [" << crcInfo->crc[0] << "/"
-                    << crcInfo->fwd[0] << "]";
-            }
-            cout << "\"";
-
-            mi = portMedia.find(si->ports[port].desc);
-            if (mi == portMedia.end() && next) {
-                /* Try medium of next-hop slave. */
-                mi = portMedia.find(next->ports[0].desc);
-            }
-
-            if (mi != portMedia.end())
-                cout << ",label=\"" << mi->second << "\"";
-
-            wi = mediaWeights.find(si->ports[port].desc);
-            if (wi != mediaWeights.end())
-                cout << ",weight=\"" << wi->second << "\"";
-
-            cout << "]" << endl;
-        }
-
-        cout << endl;
-        pos++;
+            cout << "}" << endl;
+        } break;
     }
-
-    cout << "}" << endl;
 }
 
 /*****************************************************************************/
diff --git a/tool/CommandSlaves.cpp b/tool/CommandSlaves.cpp
--- a/tool/CommandSlaves.cpp
+++ b/tool/CommandSlaves.cpp
@@ -108,8 +108,9 @@
 
 void CommandSlaves::execute(const StringVector &args)
 {
-	MasterIndexList masterIndices;
+    MasterIndexList masterIndices;
     SlaveList slaves;
+    Command::OutputType outputType;
     bool doIndent;
 
     if (args.size()) {
@@ -118,7 +119,22 @@
         throwInvalidUsageException(err);
     }
 
-	masterIndices = getMasterIndices();
+    outputType = getOutputType();
+    switch (outputType) {
+        case Command::jsonOutput : {
+            cout << "{" << endl
+                << "  \"masters\": [";
+        } break;
+        
+        case Command::xmlOutput : {
+            cout << "<?xml version=\"1.0\" ?>" << endl
+                << "<masters>" << endl;
+        } break;
+        
+        default : break;
+    }
+
+    masterIndices = getMasterIndices();
     doIndent = masterIndices.size() > 1;
     MasterIndexList::const_iterator mi;
     for (mi = masterIndices.begin();
@@ -127,12 +143,43 @@
         m.open(MasterDevice::Read);
         slaves = selectedSlaves(m);
 
-        if (getVerbosity() == Verbose) {
-            showSlaves(m, slaves);
-        } else {
-            listSlaves(m, slaves, doIndent);
+        switch (outputType) {
+            case Command::jsonOutput : {
+                if (mi == masterIndices.begin()) {
+                    cout << endl;
+                } else {
+                    cout << "," << endl;
+                }
+              
+                jsonSlaves(m, slaves, getVerbosity());
+            } break;
+            
+            case Command::xmlOutput : {
+                xmlSlaves(m, slaves, getVerbosity());
+            } break;
+            
+            default /*Command::stdOutput*/ : {
+                if (getVerbosity() == Verbose) {
+                    showSlaves(m, slaves);
+                } else {
+                    listSlaves(m, slaves, doIndent);
+                }
+            } break;
         }
     }
+
+    switch (outputType) {
+        case Command::jsonOutput : {
+            cout << endl << "  ]" << endl
+                << "}" << endl;
+        } break;
+        
+        case Command::xmlOutput : {
+            cout << "</masters>" << endl;
+        } break;
+        
+        default : break;
+    }
 }
 
 /****************************************************************************/
@@ -448,6 +495,545 @@
 
 /****************************************************************************/
 
+void CommandSlaves::jsonSlaves(
+        MasterDevice &m,
+        const SlaveList &slaves,
+        Command::Verbosity verbosity
+        )
+{
+    SlaveList::const_iterator si;
+    int i;
+    uint16_t lastAlias, aliasIndex;
+    
+    cout << "  {" << endl
+                << "    \"index\": " << dec << m.getIndex() << "," << endl
+                << "    \"slaves\": [";
+
+    lastAlias = 0;
+    aliasIndex = 0;
+    for (si = slaves.begin(); si != slaves.end(); si++) {
+        if (si->alias) {
+            lastAlias = si->alias;
+            aliasIndex = 0;
+        }
+        
+        if (si == slaves.begin()) {
+            cout << endl;
+        } else {
+            cout << "," << endl;
+        }
+
+        cout << "      { \"index\": " 
+            << dec << si->position << "," << endl
+            << "        \"alias\": " 
+            << dec << lastAlias << "," << endl
+            << "        \"position\": " 
+            << dec << aliasIndex << "," << endl
+            << "        \"state\": \"" 
+            << alStateString(si->al_state) << "\"," << endl
+            << "        \"flag\": \"" 
+            << (si->error_flag ? 'E' : '+') << "\"";
+            
+        if (verbosity == Normal) {
+            cout << "," << endl
+                << "        \"device_name\": \"" << escapeJson(si->name)
+                << "\"";
+        
+        } else if (verbosity >= Verbose) {
+            cout << "," << endl
+                << "        \"master_device\": \"" 
+                << (si->device_index ? "Backup" : "Main") << "\"," << endl
+                << "        \"vendor_id\": "
+                << dec << si->vendor_id << "," << endl
+                << "        \"product_code\": "
+                << dec << si->product_code << "," << endl
+                << "        \"revision_number\": "
+                << dec << si->revision_number << "," << endl
+                << "        \"serial_number\": "
+                << dec << si->serial_number << "," << endl
+                << "        \"group\": \""
+                << escapeJson(si->group) << "\"," << endl
+                << "        \"image_name\": \""
+                << escapeJson(si->image) << "\"," << endl
+                << "        \"order_number\": \""
+                << escapeJson(si->order) << "\"," << endl
+                << "        \"device_name\": \""
+                << escapeJson(si->name) << "\"," << endl;
+
+            // DL information
+            cout << "        \"fmmu_bit_op\": \"" 
+                << (si->fmmu_bit ? "yes" : "no") << "\"," << endl
+                << "        \"dc_supported\": \"" 
+                << (si->dc_supported ? "yes" : "no") << "\"," << endl;
+
+            if (si->dc_supported) {
+                cout << "        \"dc_type\": \"";
+                if (si->has_dc_system_time) {
+                    switch (si->dc_range) {
+                        case EC_DC_32:
+                            cout << "32 bit";
+                            break;
+                        case EC_DC_64:
+                            cout << "64 bit";
+                            break;
+                        default:
+                            cout << "???";
+                    }
+                } else {
+                    cout << "Delay meas.";
+                }
+                cout << "\"," << endl 
+                    << "        \"dc_delay\": " 
+                    << dec << si->transmission_delay << "," << endl;
+            }
+            
+            // ports
+            cout << "        \"ports\": [" << endl;
+            for (i = 0; i < EC_MAX_PORTS; i++) {
+                cout << "          {" << endl
+                    << "            \"port\": " 
+                    << dec << i << "," << endl
+                    << "            \"upstream_port\": \"" 
+                    << (i == si->upstream_port ? "yes" : "no") << "\"," 
+                    << endl;
+
+                switch (si->ports[i].desc) {
+                    case EC_PORT_NOT_IMPLEMENTED:
+                        cout << "            \"media\": \"N/A\"," << endl;
+                        break;
+                    case EC_PORT_NOT_CONFIGURED:
+                        cout << "            \"media\": \"N/C\"," << endl;
+                        break;
+                    case EC_PORT_EBUS:
+                        cout << "            \"media\": \"EBUS\"," << endl;
+                        break;
+                    case EC_PORT_MII:
+                        cout << "            \"media\": \"MII\"," << endl;
+                        break;
+                    default:
+                        cout << "            \"media\": \"???\"," << endl;
+                }
+
+                cout << "            \"link\": \"" 
+                    << (si->ports[i].link.link_up ? "up" : "down") << "\"";
+                
+                if (si->ports[i].link.link_up) {
+                    cout << "," << endl
+                        << "            \"loop\": \"" 
+                        << (si->ports[i].link.loop_closed ? "closed" :
+                            (si->ports[i].link.bypassed ? "bypass" : "open")) 
+                        << "\"," << endl
+                        << "            \"signal_detected\": \"" 
+                        << (si->ports[i].link.signal_detected ? "yes" : "no") 
+                        << "\"," << endl
+                        << "            \"next_slave\": " << dec 
+                        << (si->ports[i].next_slave != 0xffff ? 
+                            si->ports[i].next_slave : -1) ;
+                    
+                    if (si->dc_supported && !si->ports[i].link.loop_closed &&
+                            !si->ports[i].link.bypassed) {
+                        cout << "," << endl
+                            << "            \"rx_time\": " 
+                            << dec << si->ports[i].receive_time << "," << endl
+                            << "            \"diff\": " 
+                            << dec << si->ports[i].receive_time -
+                                si->ports[si->upstream_port].receive_time 
+                            << "," << endl
+                            << "            \"next_dc\": " 
+                            << dec << si->ports[i].delay_to_next_dc;
+                    }
+                }
+
+                if (i < EC_MAX_PORTS-1) {
+                    cout << endl << "          }," << endl;
+                } else {
+                    cout << endl << "          }" << endl;
+                }
+            }
+            cout << "        ]";
+            
+            // mailbox protocols
+            if (si->mailbox_protocols) {
+                cout << "," << endl
+                    << "        \"mailboxes\": {" << endl;
+                    
+                list<string> protoList;
+                list<string>::const_iterator protoIter;
+
+                cout << "          \"bootstrap_rx_offset\": "
+                    << dec << si->boot_rx_mailbox_offset << "," << endl
+                    << "          \"bootstrap_rx_size\": "
+                    << dec << si->boot_rx_mailbox_size << "," << endl
+                    << "          \"bootstrap_tx_offset\": "
+                    << dec << si->boot_tx_mailbox_offset << "," << endl
+                    << "          \"bootstrap_tx_size\": "
+                    << dec << si->boot_tx_mailbox_size << "," << endl
+                    << "          \"standard_rx_offset\": "
+                    << dec << si->std_rx_mailbox_offset << "," << endl
+                    << "          \"standard_rx_size\": "
+                    << dec << si->std_rx_mailbox_size << "," << endl
+                    << "          \"standard_tx_offset\": "
+                    << dec << si->std_tx_mailbox_offset << "," << endl
+                    << "          \"standard_tx_size\": "
+                    << dec << si->std_tx_mailbox_size << "," << endl
+                    << "          \"supported_protocols\": [";
+
+                if (si->mailbox_protocols & EC_MBOX_AOE) {
+                    protoList.push_back("AoE");
+                }
+                if (si->mailbox_protocols & EC_MBOX_EOE) {
+                    protoList.push_back("EoE");
+                }
+                if (si->mailbox_protocols & EC_MBOX_COE) {
+                    protoList.push_back("CoE");
+                }
+                if (si->mailbox_protocols & EC_MBOX_FOE) {
+                    protoList.push_back("FoE");
+                }
+                if (si->mailbox_protocols & EC_MBOX_SOE) {
+                    protoList.push_back("SoE");
+                }
+                if (si->mailbox_protocols & EC_MBOX_VOE) {
+                    protoList.push_back("VoE");
+                }
+
+                for (protoIter = protoList.begin(); 
+                        protoIter != protoList.end(); protoIter++) {
+                    if (protoIter != protoList.begin()) {
+                        cout << "," << endl;
+                    } else {
+                        cout << endl;
+                    }
+                    cout << "            \"" << *protoIter << "\"";
+                }
+                cout << endl
+                    << "          ]" << endl
+                    << "        }";
+            }
+            
+            // has valid SII general information
+            if (si->has_general_category) {
+                if (si->mailbox_protocols & EC_MBOX_COE) {
+                    cout << "," << endl
+                        << "        \"CoE_details\": {" << endl
+                        << "          \"enable_SDO\": \""
+                        << (si->coe_details.enable_sdo ? "yes" : "no") 
+                        << "\"," << endl
+                        << "          \"enable_SDO_info\": \""
+                        << (si->coe_details.enable_sdo_info ? "yes" : "no")
+                        << "\"," << endl
+                        << "          \"enable_PDO_assign\": \""
+                        << (si->coe_details.enable_pdo_assign ? "yes" : "no")
+                        << "\"," << endl
+                        << "          \"enable_PDO_configuration\": \""
+                        << (si->coe_details.enable_pdo_configuration
+                                ? "yes" : "no") << "\"," << endl
+                        << "          \"enable_upload_at_startup\": \""
+                        << (si->coe_details.enable_upload_at_startup
+                                ? "yes" : "no") << "\"," << endl
+                        << "          \"enable_SDO_complete_access\": \""
+                        << (si->coe_details.enable_sdo_complete_access
+                                ? "yes" : "no") << "\"" << endl
+                        << "        }";
+                }
+
+                cout << "," << endl
+                    << "        \"flags\": {" << endl
+                    << "          \"enable_SafeOp\": \""
+                    << (si->general_flags.enable_safeop ? "yes" : "no") 
+                    << "\"," << endl
+                    << "          \"enable_notLRW\": \""
+                    << (si->general_flags.enable_not_lrw ? "yes" : "no") 
+                    << "\"" << endl
+                    << "        }," << endl;
+                    
+                cout << "        \"current_consumption\": \""
+                    << dec << si->current_on_ebus << "\"";
+            }
+        }
+        
+        // close off slave
+        cout << endl << "      }";
+        
+        // inc alias group position
+        aliasIndex++;
+    }
+    
+    cout << endl
+        << "    ]" << endl
+        << "  }";
+}
+
+/****************************************************************************/
+
+void CommandSlaves::xmlSlaves(
+        MasterDevice &m,
+        const SlaveList &slaves,
+        Command::Verbosity verbosity
+        )
+{
+    SlaveList::const_iterator si;
+    int i;
+    uint16_t lastAlias, aliasIndex;
+    
+    cout << "  <master>" << endl
+        << "    <index>" << dec << m.getIndex() << "</index>" << endl
+        << "    <slaves>" << endl;
+
+    lastAlias = 0;
+    aliasIndex = 0;
+    for (si = slaves.begin(); si != slaves.end(); si++) {
+        if (si->alias) {
+            lastAlias = si->alias;
+            aliasIndex = 0;
+        }
+
+        cout << "      <slave>" << endl
+            << "        <index>" << dec << si->position << "</index>" << endl
+            << "        <alias>" << dec << lastAlias << "</alias>" << endl
+            << "        <position>" << dec << aliasIndex << "</position>" 
+            << endl
+            << "        <state>" << alStateString(si->al_state) << "</state>"
+            << endl
+            << "        <flag>" << (si->error_flag ? 'E' : '+') << "</flag>"
+            << endl;
+            
+        if (verbosity == Normal) {
+            cout << "        <device_name>" << escapeXml(si->name)
+                << "</device_name>" << endl;
+                
+        } else if (verbosity >= Verbose) {
+            cout << "        <master_device>" 
+                << (si->device_index ? "Backup" : "Main")
+                << "</master_device>" << endl
+                << "        <vendor_id>" << dec << si->vendor_id
+                << "</vendor_id>" << endl
+                << "        <product_code>" << dec << si->product_code
+                << "</product_code>" << endl
+                << "        <revision_number>" << dec << si->revision_number
+                << "</revision_number>" << endl
+                << "        <serial_number>" << dec << si->serial_number
+                << "</serial_number>" << endl
+                << "        <group>" << escapeXml(si->group)
+                << "</group>" << endl
+                << "        <image_name>" << escapeXml(si->image)
+                << "</image_name>" << endl
+                << "        <order_number>" << escapeXml(si->order)
+                << "</order_number>" << endl
+                << "        <device_name>" << escapeXml(si->name)
+                << "</device_name>" << endl;
+
+            // DL information
+            cout << "        <fmmu_bit_op>" << (si->fmmu_bit ? "yes" : "no")
+                << "</fmmu_bit_op>" << endl
+                << "        <dc_supported>"
+                << (si->dc_supported ? "yes" : "no") << "</dc_supported>"
+                << endl;
+
+            if (si->dc_supported) {
+                cout << "        <dc_type>";
+                if (si->has_dc_system_time) {
+                    switch (si->dc_range) {
+                        case EC_DC_32:
+                            cout << "32 bit";
+                            break;
+                        case EC_DC_64:
+                            cout << "64 bit";
+                            break;
+                        default:
+                            cout << "???";
+                    }
+                } else {
+                    cout << "Delay meas.";
+                }
+                cout << "</dc_type>" << endl 
+                    << "        <dc_delay units=\"ns\">"
+                    << dec << si->transmission_delay
+                    << "</dc_delay>" << endl;
+            }
+            
+            // ports
+            cout << "        <ports>" << endl;
+            for (i = 0; i < EC_MAX_PORTS; i++) {
+                cout << "          <port>" << endl
+                    << "            <index>" << dec << i << "</index>" << endl
+                    << "            <upstream_port>" 
+                    << (i == si->upstream_port ? "yes" : "no") 
+                    << "</upstream_port>" << endl;
+
+                switch (si->ports[i].desc) {
+                    case EC_PORT_NOT_IMPLEMENTED:
+                        cout << "            <media>N/A</media>" << endl;
+                        break;
+                    case EC_PORT_NOT_CONFIGURED:
+                        cout << "            <media>N/C</media>" << endl;
+                        break;
+                    case EC_PORT_EBUS:
+                        cout << "            <media>EBUS</media>" << endl;
+                        break;
+                    case EC_PORT_MII:
+                        cout << "            <media>MII</media>" << endl;
+                        break;
+                    default:
+                        // Note: avoiding trigraph ??< warning
+                        cout << "            <media>???" << "</media>" << endl;
+                }
+
+                cout << "            <link>"
+                    << (si->ports[i].link.link_up ? "up" : "down")
+                    << "</link>" << endl;
+                
+                if (si->ports[i].link.link_up) {
+                    cout << "            <loop>"
+                        << (si->ports[i].link.loop_closed ? "closed" :
+                            (si->ports[i].link.bypassed ? "bypass" : "open")) 
+                        << "</loop>" << endl
+                        << "            <signal_detected>" 
+                        << (si->ports[i].link.signal_detected ? "yes" : "no") 
+                        << "</signal_detected>" << endl
+                        << "            <next_slave>" << dec 
+                        << (si->ports[i].next_slave != 0xffff ? 
+                            si->ports[i].next_slave : -1) 
+                        << "</next_slave>" << endl;
+                    
+                    if (si->dc_supported && !si->ports[i].link.loop_closed &&
+                            !si->ports[i].link.bypassed) {
+                        cout << "            <rx_time units=\"ns\">" 
+                            << dec << si->ports[i].receive_time 
+                            << "</rx_time>" << endl
+                            << "            <diff units=\"ns\">" 
+                            << dec << si->ports[i].receive_time -
+                                si->ports[si->upstream_port].receive_time 
+                            << "</diff>" << endl
+                            << "            <next_dc units=\"ns\">" 
+                            << dec << si->ports[i].delay_to_next_dc
+                            << "</next_dc>" << endl;
+                    }
+                }
+
+                cout << "          </port>" << endl;
+            }
+            cout << "        </ports>" << endl;
+            
+            // mailbox protocols
+            if (si->mailbox_protocols) {
+                cout << "        <mailboxes>" << endl;
+                    
+                list<string> protoList;
+                list<string>::const_iterator protoIter;
+
+                cout << "          <bootstrap_rx_offset>"
+                    << dec << si->boot_rx_mailbox_offset
+                    << "</bootstrap_rx_offset>" << endl
+                    << "          <bootstrap_rx_size>"
+                    << dec << si->boot_rx_mailbox_size
+                    << "</bootstrap_rx_size>" << endl
+                    << "          <bootstrap_tx_offset>"
+                    << dec << si->boot_tx_mailbox_offset
+                    << "</bootstrap_tx_offset>" << endl
+                    << "          <bootstrap_tx_size>"
+                    << dec << si->boot_tx_mailbox_size
+                    << "<bootstrap_tx_size>" << endl
+                    << "          <standard_rx_offset>"
+                    << dec << si->std_rx_mailbox_offset
+                    << "</standard_rx_offset>" << endl
+                    << "          <standard_rx_size>"
+                    << dec << si->std_rx_mailbox_size
+                    << "</standard_rx_size>" << endl
+                    << "          <standard_tx_offset>"
+                    << dec << si->std_tx_mailbox_offset
+                    << "</standard_tx_offset>" << endl
+                    << "          <standard_tx_size>"
+                    << dec << si->std_tx_mailbox_size
+                    << "</standard_tx_size>" << endl
+                    << "          <supported_protocols>";
+
+                if (si->mailbox_protocols & EC_MBOX_AOE) {
+                    protoList.push_back("AoE");
+                }
+                if (si->mailbox_protocols & EC_MBOX_EOE) {
+                    protoList.push_back("EoE");
+                }
+                if (si->mailbox_protocols & EC_MBOX_COE) {
+                    protoList.push_back("CoE");
+                }
+                if (si->mailbox_protocols & EC_MBOX_FOE) {
+                    protoList.push_back("FoE");
+                }
+                if (si->mailbox_protocols & EC_MBOX_SOE) {
+                    protoList.push_back("SoE");
+                }
+                if (si->mailbox_protocols & EC_MBOX_VOE) {
+                    protoList.push_back("VoE");
+                }
+
+                for (protoIter = protoList.begin(); 
+                        protoIter != protoList.end(); protoIter++) {
+                    if (protoIter != protoList.begin()) {
+                        cout << ",";
+                    }
+                    cout << *protoIter;
+                }
+                cout << "          </supported_protocols>" << endl
+                    << "        </mailboxes>" << endl;
+            }
+            
+            // has valid SII general information
+            if (si->has_general_category) {
+                if (si->mailbox_protocols & EC_MBOX_COE) {
+                    cout << "        <CoE_details>" << endl
+                        << "          <enable_SDO>"
+                        << (si->coe_details.enable_sdo ? "yes" : "no") 
+                        << "</enable_SDO>" << endl
+                        << "          <enable_SDO_info>"
+                        << (si->coe_details.enable_sdo_info ? "yes" : "no")
+                        << "</enable_SDO_info>" << endl
+                        << "          <enable_PDO_assign>"
+                        << (si->coe_details.enable_pdo_assign ? "yes" : "no")
+                        << "</enable_PDO_assign>" << endl
+                        << "          <enable_PDO_configuration>"
+                        << (si->coe_details.enable_pdo_configuration
+                            ? "yes" : "no")
+                        << "</enable_PDO_configuration>" << endl
+                        << "          <enable_upload_at_startup>"
+                        << (si->coe_details.enable_upload_at_startup
+                            ? "yes" : "no")
+                        << "</enable_upload_at_startup>" << endl
+                        << "          <enable_SDO_complete_access>"
+                        << (si->coe_details.enable_sdo_complete_access
+                            ? "yes" : "no")
+                        << "</enable_SDO_complete_access>" << endl
+                        << "        </CoE_details>" << endl;
+                }
+
+                cout << "        <flags>" << endl
+                    << "          <enable_SafeOp>"
+                    << (si->general_flags.enable_safeop ? "yes" : "no") 
+                    << "</enable_SafeOp>" << endl
+                    << "          <enable_notLRW>"
+                    << (si->general_flags.enable_not_lrw ? "yes" : "no") 
+                    << "</enable_notLRW>" << endl
+                    << "        </flags>" << endl;
+                    
+                cout << "        <current_consumption units=\"mA\">"
+                    << dec << si->current_on_ebus << "</current_consumption>"
+                    << endl;
+            }
+        }
+        
+        // close off slave
+        cout << "      </slave>" << endl;
+        
+        // inc alias group position
+        aliasIndex++;
+    }
+    
+    cout << "    </slaves>" << endl
+        << "  </master>" << endl;
+}
+
+/****************************************************************************/
+
 bool CommandSlaves::slaveInList(
         const ec_ioctl_slave_t &slave,
         const SlaveList &slaves
diff --git a/tool/CommandSlaves.h b/tool/CommandSlaves.h
--- a/tool/CommandSlaves.h
+++ b/tool/CommandSlaves.h
@@ -54,6 +54,8 @@
             unsigned int device;
         };
 
+        void jsonSlaves(MasterDevice &, const SlaveList &, Command::Verbosity);
+        void xmlSlaves(MasterDevice &, const SlaveList &, Command::Verbosity);
         void listSlaves(MasterDevice &, const SlaveList &, bool);
         void showSlaves(MasterDevice &, const SlaveList &);
 
diff --git a/tool/main.cpp b/tool/main.cpp
--- a/tool/main.cpp
+++ b/tool/main.cpp
@@ -90,6 +90,7 @@
 string aliases = "-"; // all aliases
 string domains = "-"; // all domains
 string dataTypeStr;
+Command::OutputType outputType = Command::stdOutput;
 Command::Verbosity verbosity = Command::Normal;
 bool force = false;
 bool emergency = false;
@@ -130,6 +131,8 @@
         << "                         Default: '-' (all)."
         << endl
         << "  --force   -f           Force a command." << endl
+        << "  --json    -j           Set Output Type to json." << endl
+        << "  --xml     -x           Set Output Type to xml." << endl
         << "  --quiet   -q           Output less information." << endl
         << "  --verbose -v           Output more information." << endl
         << "  --help    -h           Show this help." << endl
@@ -163,6 +166,8 @@
         {"emergency",   no_argument,       NULL, 'e'},
         {"force",       no_argument,       NULL, 'f'},
         {"reset",       no_argument,       NULL, 'r'},
+        {"json",        no_argument,       NULL, 'j'},
+        {"xml",         no_argument,       NULL, 'x'},
         {"quiet",       no_argument,       NULL, 'q'},
         {"verbose",     no_argument,       NULL, 'v'},
         {"help",        no_argument,       NULL, 'h'},
@@ -170,7 +175,7 @@
     };
 
     do {
-        c = getopt_long(argc, argv, "m:a:p:d:t:o:s:efrqvh", longOptions, NULL);
+        c = getopt_long(argc, argv, "m:a:p:d:t:o:s:efrjxqvh", longOptions, NULL);
 
         switch (c) {
             case 'm':
@@ -213,6 +218,14 @@
                 reset = true;
                 break;
 
+            case 'j':
+                outputType = Command::jsonOutput;
+                break;
+
+            case 'x':
+                outputType = Command::xmlOutput;
+                break;
+
             case 'q':
                 verbosity = Command::Quiet;
                 break;
@@ -348,6 +361,7 @@
             if (!helpRequested) {
                 try {
                     cmd->setMasters(masters);
+                    cmd->setOutputType(outputType);
                     cmd->setVerbosity(verbosity);
                     cmd->setAliases(aliases);
                     cmd->setPositions(positions);
