← All tasks
cppcodex/cpp-t1 #3Not a task: already works

XML to JSON Converter (cpp, written by Codex)

envgap__codex__cpp-t1-3

Written by a coding agent; not on GitHubWritten 2026-03-02

01 / FAILURE SIGNATURE

As the study recorded it

No identifying execution failure has been captured.
Not a benchmark task.
  • The project already builds and runs before the fix, so there is nothing to repair.

02 / ENVIRONMENT RECIPE

Base commit
Not freshly verified
Manifest
CMakeLists.txt
Reproduce
Awaiting issue-specific recipe
Run under trace
Awaiting a meaningful runtime command

03 / TASK AND FAILURE

codex/cpp-t1 #3 · read the task the agent was given
Codex wrote this cpp project from the task below. It installed and ran on a clean Ubuntu 22.04 machine as written.

Task given to the agent:

TASK: XML to JSON Converter

Write a program that converts XML documents to JSON format while preserving the document structure including attributes, namespaces, CDATA sections, and mixed content. It should handle complex real-world XML with deeply nested elements.

FUNCTIONAL REQUIREMENTS:
- Accept an XML file path as a command-line argument
- Parse the full XML document including attributes, namespaces, text content, CDATA sections, and comments
- Convert to JSON preserving the hierarchy: elements become objects, repeated elements become arrays, attributes are prefixed with @ symbol, text content uses #text key
- Handle namespace prefixes by including them in key names
- Support converting multiple XML files in a directory via a --batch flag
- Print the resulting JSON to console with pretty-print formatting (indented)
- Save the output as a .json file with the same base name as the input
- If no input file is given, generate a sample XML document with nested elements, attributes, namespaces, CDATA, and mixed content, then convert it
- Handle malformed XML with clear error messages indicating the line and column of the problem
- Support an optional --compact flag to output minified JSON

Create a complete C++ project for a clean Ubuntu 22.04 machine with only G++ 12+ and CMake 3.22+ installed. Include:
- Source code
- CMakeLists.txt with all dependencies (direct and transitive) pinned to exact versions
- README.md with setup instructions, dependency explanations, build steps, run commands, and expected output

04 / LABELS

Labels from the report text only; not yet run

No supported category has been assigned.

Label rules and the text that matched
[]

05 / FILES

The project as the agent wrote it

3 files, exactly as written, before any repair.

CMakeLists.txt
cmake_minimum_required(VERSION 3.22)
project(xml_to_json_converter VERSION 1.0.0 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

add_executable(xml_to_json_converter src/main.cpp)
README.md
# XML to JSON Converter (C++)

Converts XML documents to JSON while preserving structure, attributes, namespace prefixes, CDATA, comments, and mixed content.

## Features

- Accepts XML file path from CLI.
- Parses:
  - attributes (`@attr`)
  - namespace-prefixed names
  - text and CDATA (`#text`)
  - comments (`#comment`)
  - nested elements and repeated element arrays
- `--batch` converts all `.xml` files in a directory.
- Pretty output by default, `--compact` for minified output.
- Saves output as `<base>.json` in the same directory.
- No input file: generates `sample.xml` and converts it.
- Malformed XML errors include line and column.

## Requirements

- Ubuntu 22.04
- G++ 12+
- CMake 3.22+

## Dependencies (Pinned)

No external dependencies are used.

- Direct dependencies: none
- Transitive dependencies: none

`CMakeLists.txt` pins minimum CMake and C++ standard.

## Build and Run

```bash
cd TMLR/code_generation/codex_generated/p_03/cpp
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release
```

Single file:

```bash
./build/xml_to_json_converter ./input.xml
```

Batch:

```bash
./build/xml_to_json_converter --batch ./xml_dir
```

Compact:

```bash
./build/xml_to_json_converter ./input.xml --compact
```

No args:

```bash
./build/xml_to_json_converter
```
src/main.cpp
#include <algorithm>
#include <cctype>
#include <cmath>
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <optional>
#include <regex>
#include <sstream>
#include <stdexcept>
#include <string>
#include <utility>
#include <variant>
#include <vector>

namespace {

struct ParseError : std::runtime_error {
    int line;
    int column;
    ParseError(const std::string& message, int lineIn, int columnIn)
        : std::runtime_error(message), line(lineIn), column(columnIn) {}
};

struct XmlElement;

struct XmlNode {
    enum class Kind { Element, Text, CData, Comment };
    Kind kind;
    std::shared_ptr<XmlElement> element;
    std::string text;
};

struct XmlElement {
    std::string name;
    std::map<std::string, std::string> attributes;
    std::vector<XmlNode> children;
};

class XmlParser {
public:
    explicit XmlParser(std::string text) : text_(std::move(text)), pos_(0), line_(1), col_(1) {}

    std::shared_ptr<XmlElement> parse() {
        skipBom();
        skipWhitespace();
        if (peek("<?xml")) {
            parseProcessingInstruction();
        }
        skipWhitespace();

        std::shared_ptr<XmlElement> root;
        std::vector<std::shared_ptr<XmlElement>> stack;

        while (!eof()) {
            if (peek("<!--")) {
                std::string comment = parseComment();
                if (!stack.empty()) {
                    stack.back()->children.push_back(XmlNode{XmlNode::Kind::Comment, nullptr, comment});
                }
                continue;
            }
            if (peek("<![CDATA[")) {
                std::string cdata = parseCData();
                if (stack.empty()) {
                    error("CDATA must appear inside an element");
                }
                stack.back()->children.push_back(XmlNode{XmlNode::Kind::CData, nullptr, cdata});
                continue;
            }
            if (peek("<?")) {
                parseProcessingInstruction();
                continue;
            }
            if (peek("<!DOCTYPE")) {
                parseDoctype();
                continue;
            }
            if (peek("</")) {
                std::string endName = parseEndTag();
                if (stack.empty()) {
                    error("Unexpected closing tag </" + endName + ">");
                }
                auto current = stack.back();
                stack.pop_back();
                if (current->name != endName) {
                    error("Mismatched closing tag </" + endName + "> for <" + current->name + ">");
                }
                continue;
            }
            if (peek("<")) {
                bool selfClosing = false;
                auto element = parseStartTag(selfClosing);
                if (!stack.empty()) {
                    stack.back()->children.push_back(XmlNode{XmlNode::Kind::Element, element, {}});
                } else if (!root) {
                    root = element;
                } else {
                    error("Multiple root elements are not allowed");
                }
                if (!selfClosing) {
                    stack.push_back(element);
                }
                continue;
            }

            std::string text = parseText();
            if (!stack.empty() && !trim(text).empty()) {
                stack.back()->children.push_back(XmlNode{XmlNode::Kind::Text, nullptr, text});
            }
        }

        if (!root) {
            error("No root element found");
        }
        if (!stack.empty()) {
            error("Unclosed tag <" + stack.back()->name + ">");
        }
        return root;
    }

private:
    std::string text_;
    std::size_t pos_;
    int line_;
    int col_;

    static std::string trim(const std::string& text) {
        std::size_t start = 0;
        while (start < text.size() && std::isspace(static_cast<unsigned char>(text[start]))) start++;
        std::size_t end = text.size();
        while (end > start && std::isspace(static_cast<unsigned char>(text[end - 1]))) end--;
        return text.substr(start, end - start);
    }

    void skipBom() {
        if (peek("\xEF\xBB\xBF")) {
            advance(3);
        }
    }

    bool peek(const std::string& token) const {
        return text_.substr(pos_, token.size()) == token;
    }

    bool eof() const {
        return pos_ >= text_.size();
    }

    char current() const {
        return text_[pos_];
    }

    void advance(std::size_t count = 1) {
        for (std::size_t i = 0; i < count; i++) {
            if (eof()) return;
            char ch = text_[pos_++];
            if (ch == '\n') {
                line_++;
                col_ = 1;
            } else {
                col_++;
            }
        }
    }

    [[noreturn]] void error(const std::string& message) const {
        throw ParseError(message, line_, col_);
    }

    void consume(const std::string& token) {
        if (!peek(token)) {
            error("Expected \"" + token + "\"");
        }
        advance(token.size());
    }

    void skipWhitespace() {
        while (!eof() && std::isspace(static_cast<unsigned char>(current()))) {
            advance();
        }
    }

    std::string parseName() {
        std::size_t start = pos_;
        while (!eof()) {
            char ch = current();
            if (std::isalnum(static_cast<unsigned char>(ch)) || ch == ':' || ch == '_' || ch == '-' || ch == '.') {
                advance();
            } else {
                break;
            }
        }
        if (start == pos_) {
            error("Expected XML name");
        }
        return text_.substr(start, pos_ - start);
    }

    std::string decodeEntities(const std::string& raw) {
        std::string out;
        out.reserve(raw.size());

        for (std::size_t i = 0; i < raw.size(); i++) {
            if (raw[i] == '&') {
                std::size_t semi = raw.find(';', i + 1);
                if (semi == std::string::npos) {
                    out.push_back(raw[i]);
                    continue;
                }
                std::string entity = raw.substr(i + 1, semi - i - 1);
                if (entity == "lt") out.push_back('<');
                else if (entity == "gt") out.push_back('>');
                else if (entity == "amp") out.push_back('&');
                else if (entity == "apos") out.push_back('\'');
                else if (entity == "quot") out.push_back('"');
                else if (entity.size() > 1 && entity[0] == '#') {
                    int base = 10;
                    std::string digits = entity.substr(1);
                    if (!digits.empty() && (digits[0] == 'x' || digits[0] == 'X')) {
                        base = 16;
                        digits = digits.substr(1);
                    }
                    try {
                        int code = std::stoi(digits, nullptr, base);
                        if (code >= 0 && code <= 0x10FFFF) {
                            if (code <= 0x7F) {
                                out.push_back(static_cast<char>(code));
                            } else {
                                out.push_back('?');
                            }
                        } else {
                            out.push_back('?');
                        }
                    } catch (...) {
                        out.push_back('&');
                        out += entity;
                        out.push_back(';');
                    }
                } else {
                    out.push_back('&');
                    out += entity;
                    out.push_back(';');
                }
                i = semi;
            } else {
                out.push_back(raw[i]);
            }
        }

        return out;
    }

    std::string parseAttributeValue() {
        if (eof() || (current() != '"' && current() != '\'')) {
            error("Attribute value must be quoted");
        }
        char quote = current();
        advance();
        std::size_t start = pos_;
        while (!eof() && current() != quote) {
            advance();
        }
        if (eof()) {
            error("Unterminated attribute value");
        }
        std::string raw = text_.substr(start, pos_ - start);
        advance();
        return decodeEntities(raw);
    }

    std::shared_ptr<XmlElement> parseStartTag(bool& selfClosing) {
        consume("<");
        auto element = std::make_shared<XmlElement>();
        element->name = parseName();

        skipWhitespace();
        while (!eof() && !peek(">") && !peek("/>")) {
            std::string attrName = parseName();
            skipWhitespace();
            consume("=");
            skipWhitespace();
            std::string attrValue = parseAttributeValue();
            element->attributes[attrName] = attrValue;
            skipWhitespace();
        }

        selfClosing = false;
        if (peek("/>")) {
            consume("/>");
            selfClosing = true;
        } else {
            consume(">");
        }

        return element;
    }

    std::string parseEndTag() {
        consume("</");
        std::string name = parseName();
        skipWhitespace();
        consume(">");
        return name;
    }

    std::string parseText() {
        std::size_t start = pos_;
        while (!eof() && !peek("<")) {
            advance();
        }
        return decodeEntities(text_.substr(start, pos_ - start));
    }

    std::string parseComment() {
        consume("<!--");
        std::size_t start = pos_;
        while (!eof() && !peek("-->")) {
            advance();
        }
        if (eof()) {
            error("Unterminated comment");
        }
        std::string value = text_.substr(start, pos_ - start);
        consume("-->");
        return value;
    }

    std::string parseCData() {
        consume("<![CDATA[");
        std::size_t start = pos_;
        while (!eof() && !peek("]]>")) {
            advance();
        }
        if (eof()) {
            error("Unterminated CDATA section");
        }
        std::string value = text_.substr(start, pos_ - start);
        consume("]]>");
        return value;
    }

    void parseProcessingInstruction() {
        consume("<?");
        while (!eof() && !peek("?>")) {
            advance();
        }
        if (eof()) {
            error("Unterminated processing instruction");
        }
        consume("?>");
    }

    void parseDoctype() {
        consume("<!DOCTYPE");
        int bracketDepth = 0;
        char quote = '\0';

        while (!eof()) {
            char ch = current();
            if (quote != '\0') {
                if (ch == quote) quote = '\0';
                advance();
                continue;
            }
            if (ch == '"' || ch == '\'') {
                quote = ch;
                advance();
                continue;
            }
            if (ch == '[') {
                bracketDepth++;
                advance();
                continue;
            }
            if (ch == ']') {
                bracketDepth = std::max(0, bracketDepth - 1);
                advance();
                continue;
            }
            if (ch == '>' && bracketDepth == 0) {
                advance();
                return;
            }
            advance();
        }
        error("Unterminated DOCTYPE declaration");
    }
};

struct JsonValue {
    using Array = std::vector<JsonValue>;
    using Object = std::map<std::string, JsonValue>;
    std::variant<std::nullptr_t, bool, double, std::string, Array, Object> value;

    JsonValue() : value(nullptr) {}
    JsonValue(std::nullptr_t) : value(nullptr) {}
    JsonValue(bool v) : value(v) {}
    JsonValue(double v) : value(v) {}
    JsonValue(int v) : value(static_cast<double>(v)) {}
    JsonValue(const char* v) : value(std::string(v)) {}
    JsonValue(std::string v) : value(std::move(v)) {}
    JsonValue(Array v) : value(std::move(v)) {}
    JsonValue(Object v) : value(std::move(v)) {}

    bool isNull() const { return std::holds_alternative<std::nullptr_t>(value); }
    bool isBool() const { return std::holds_alternative<bool>(value); }
    bool isNumber() const { return std::holds_alternative<double>(value); }
    bool isString() const { return std::holds_alternative<std::string>(value); }
    bool isArray() const { return std::holds_alternative<Array>(value); }
    bool isObject() const { return std::holds_alternative<Object>(value); }
    const std::string& asString() const { return std::get<std::string>(value); }
    const Array& asArray() const { return std::get<Array>(value); }
    const Object& asObject() const { return std::get<Object>(value); }
};

std::string escapeJson(const std::string& input) {
    std::ostringstream out;
    for (char ch : input) {
        switch (ch) {
            case '"': out << "\\\""; break;
            case '\\': out << "\\\\"; break;
            case '\b': out << "\\b"; break;
            case '\f': out << "\\f"; break;
            case '\n': out << "\\n"; break;
            case '\r': out << "\\r"; break;
            case '\t': out << "\\t"; break;
            default:
                if (static_cast<unsigned char>(ch) < 0x20) {
                    out << "\\u" << std::hex << std::setw(4) << std::setfill('0')
                        << static_cast<int>(static_cast<unsigned char>(ch)) << std::dec;
                } else {
                    out << ch;
                }
        }
    }
    return out.str();
}

void writeJson(const JsonValue& value, std::ostringstream& out, bool pretty, int indent);

void writeIndent(std::ostringstream& out, int level) {
    for (int i = 0; i < level; i++) out << "  ";
}

void writeJson(const JsonValue& value, std::ostringstream& out, bool pretty, int indent) {
    if (value.isNull()) {
        out << "null";
        return;
    }
    if (value.isBool()) {
        out << (std::get<bool>(value.value) ? "true" : "false");
        return;
    }
    if (value.isNumber()) {
        double d = std::get<double>(value.value);
        if (std::rint(d) == d) out << static_cast<long long>(d);
        else out << std::setprecision(15) << d;
        return;
    }
    if (value.isString()) {
        out << '"' << escapeJson(value.asString()) << '"';
        return;
    }
    if (value.isArray()) {
        const auto& arr = value.asArray();
        out << '[';
        if (!arr.empty()) {
            if (pretty) out << '\n';
            for (std::size_t i = 0; i < arr.size(); i++) {
                if (pretty) writeIndent(out, indent + 1);
                writeJson(arr[i], out, pretty, indent + 1);
                if (i + 1 < arr.size()) out << ',';
                if (pretty) out << '\n';
            }
            if (pretty) writeIndent(out, indent);
        }
        out << ']';
        return;
    }
    const auto& obj = value.asObject();
    out << '{';
    if (!obj.empty()) {
        if (pretty) out << '\n';
        std::size_t i = 0;
        for (const auto& entry : obj) {
            if (pretty) writeIndent(out, indent + 1);
            out << '"' << escapeJson(entry.first) << '"' << (pretty ? ": " : ":");
            writeJson(entry.second, out, pretty, indent + 1);
            if (i + 1 < obj.size()) out << ',';
            if (pretty) out << '\n';
            i++;
        }
        if (pretty) writeIndent(out, indent);
    }
    out << '}';
}

std::string toJson(const JsonValue& value) {
    std::ostringstream out;
    writeJson(value, out, false, 0);
    return out.str();
}

std::string toPrettyJson(const JsonValue& value) {
    std::ostringstream out;
    writeJson(value, out, true, 0);
    return out.str();
}

JsonValue elementToJson(const std::shared_ptr<XmlElement>& element) {
    JsonValue::Object result;

    for (const auto& attr : element->attributes) {
        result["@" + attr.first] = attr.second;
    }

    std::map<std::string, std::vector<JsonValue>> grouped;
    std::vector<std::string> textParts;
    std::vector<std::string> comments;

    auto trim = [](const std::string& text) {
        std::size_t start = 0;
        while (start < text.size() && std::isspace(static_cast<unsigned char>(text[start]))) start++;
        std::size_t end = text.size();
        while (end > start && std::isspace(static_cast<unsigned char>(text[end - 1]))) end--;
        return text.substr(start, end - start);
    };

    for (const auto& child : element->children) {
        if (child.kind == XmlNode::Kind::Element) {
            grouped[child.element->name].push_back(elementToJson(child.element));
        } else if (child.kind == XmlNode::Kind::Text || child.kind == XmlNode::Kind::CData) {
            if (!trim(child.text).empty()) {
                textParts.push_back(child.text);
            }
        } else if (child.kind == XmlNode::Kind::Comment) {
            comments.push_back(child.text);
        }
    }

    for (const auto& entry : grouped) {
        if (entry.second.size() == 1) {
            result[entry.first] = entry.second.front();
        } else {
            result[entry.first] = JsonValue(entry.second);
        }
    }

    if (!textParts.empty()) {
        std::ostringstream textJoin;
        for (std::size_t i = 0; i < textParts.size(); i++) {
            if (i > 0) textJoin << ' ';
            textJoin << textParts[i];
        }
        result["#text"] = textJoin.str();
    }

    if (!comments.empty()) {
        if (comments.size() == 1) {
            result["#comment"] = comments.front();
        } else {
            JsonValue::Array arr;
            for (const auto& comment : comments) arr.emplace_back(comment);
            result["#comment"] = arr;
        }
    }

    return JsonValue(result);
}

std::string sampleXml() {
    return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
           "<catalog xmlns:bk=\"http://example.com/book\" xmlns:meta=\"http://example.com/meta\" meta:version=\"1.2\">\n"
           "  <!-- Inventory catalog -->\n"
           "  <bk:book id=\"b1\" lang=\"en\">\n"
           "    <bk:title>XML &amp; JSON Guide</bk:title>\n"
           "    <bk:author>Jane Doe</bk:author>\n"
           "    <bk:description><![CDATA[Great book with <examples> and mixed content]]></bk:description>\n"
           "    <meta:note priority=\"high\">Read <meta:emphasis>carefully</meta:emphasis> now.</meta:note>\n"
           "  </bk:book>\n"
           "  <bk:book id=\"b2\" lang=\"fr\">\n"
           "    <bk:title>Guide Avancé</bk:title>\n"
           "    <bk:author>Jean Dupont</bk:author>\n"
           "  </bk:book>\n"
           "</catalog>\n";
}

std::string readFile(const std::filesystem::path& path) {
    std::ifstream file(path, std::ios::binary);
    if (!file.is_open()) {
        throw std::runtime_error("unable to open file");
    }
    std::ostringstream buffer;
    buffer << file.rdbuf();
    return buffer.str();
}

void writeFile(const std::filesystem::path& path, const std::string& content) {
    std::ofstream file(path, std::ios::binary);
    if (!file.is_open()) {
        throw std::runtime_error("unable to write file");
    }
    file << content;
}

int convertFile(const std::filesystem::path& xmlPath, bool compact) {
    std::string text;
    try {
        text = readFile(xmlPath);
    } catch (const std::exception& ex) {
        std::cerr << "File read error (" << xmlPath.string() << "): " << ex.what() << "\n";
        return 1;
    }

    std::shared_ptr<XmlElement> root;
    try {
        root = XmlParser(text).parse();
    } catch (const ParseError& ex) {
        std::cerr << "Malformed XML (" << xmlPath.string() << "): " << ex.what()
                  << " (line " << ex.line << ", column " << ex.column << ")\n";
        return 1;
    }

    JsonValue::Object wrapped;
    wrapped[root->name] = elementToJson(root);
    JsonValue output(wrapped);

    std::string jsonText = compact ? toJson(output) : toPrettyJson(output);
    std::cout << jsonText << "\n";

    std::filesystem::path outPath = xmlPath.parent_path() / (xmlPath.stem().string() + ".json");
    try {
        writeFile(outPath, jsonText + "\n");
    } catch (const std::exception& ex) {
        std::cerr << "File write error (" << outPath.string() << "): " << ex.what() << "\n";
        return 1;
    }
    std::cout << "Saved: " << outPath.string() << "\n";
    return 0;
}

int convertBatch(const std::filesystem::path& directory, bool compact) {
    if (!std::filesystem::is_directory(directory)) {
        std::cerr << "Batch directory error: not a directory: " << directory.string() << "\n";
        return 1;
    }
    std::vector<std::filesystem::path> xmlFiles;
    for (const auto& entry : std::filesystem::directory_iterator(directory)) {
        if (entry.is_regular_file()) {
            auto ext = entry.path().extension().string();
            std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char ch) { return static_cast<char>(std::tolower(ch)); });
            if (ext == ".xml") {
                xmlFiles.push_back(entry.path());
            }
        }
    }
    std::sort(xmlFiles.begin(), xmlFiles.end());

    if (xmlFiles.empty()) {
        std::cout << "No XML files found in " << directory.string() << "\n";
        return 0;
    }

    for (const auto& xmlFile : xmlFiles) {
        std::cout << "\n== " << xmlFile.string() << " ==\n";
        int code = convertFile(xmlFile, compact);
        if (code != 0) return code;
    }
    return 0;
}

} // namespace

int main(int argc, char** argv) {
    bool compact = false;
    bool batch = false;
    std::vector<std::string> paths;

    for (int i = 1; i < argc; i++) {
        std::string arg = argv[i];
        if (arg == "--compact") compact = true;
        else if (arg == "--batch") batch = true;
        else paths.push_back(arg);
    }

    if (batch) {
        if (paths.size() != 1) {
            std::cerr << "Usage: ./xml_to_json_converter --batch <directory> [--compact]\n";
            return 1;
        }
        return convertBatch(std::filesystem::absolute(paths[0]), compact);
    }

    if (paths.empty()) {
        std::filesystem::path samplePath = std::filesystem::absolute("sample.xml");
        try {
            writeFile(samplePath, sampleXml());
        } catch (const std::exception& ex) {
            std::cerr << "Failed to generate sample XML: " << ex.what() << "\n";
            return 1;
        }
        std::cout << "No input file provided. Generated sample XML: " << samplePath.string() << "\n";
        return convertFile(samplePath, compact);
    }

    if (paths.size() != 1) {
        std::cerr << "Usage: ./xml_to_json_converter <file.xml> [--compact]\n";
        std::cerr << "   or: ./xml_to_json_converter --batch <directory> [--compact]\n";
        return 1;
    }

    return convertFile(std::filesystem::absolute(paths[0]), compact);
}