← All tasks
javacodex/java-t1 #8Not a task: already works

Multi-Format Data Converter (java, written by Codex)

envgap__codex__java-t1-8

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

01 / FAILURE SIGNATURE

As the study recorded it

None
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
pom.xml
Reproduce
Awaiting issue-specific recipe
Run under trace
Awaiting a meaningful runtime command

03 / TASK AND FAILURE

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

Task given to the agent:

TASK: Multi-Format Data Converter

Write a program that converts data files between CSV, JSON, XML, and YAML formats while preserving data types, nested structures, and handling large files efficiently.

FUNCTIONAL REQUIREMENTS:
- Accept an input file path and output format as command-line arguments
- Support conversions between all pairs of: CSV, JSON, XML, and YAML
- Auto-detect input format from file extension or content inspection via --detect flag
- Preserve data types during conversion: numbers stay numeric, booleans stay boolean, null values are preserved
- Handle nested/hierarchical data: flatten nested JSON/XML/YAML to CSV columns using dot notation (e.g., address.city), or unflatten CSV dot-notation columns back into nested structures
- Support array data in conversions: JSON arrays become CSV rows, CSV rows become JSON arrays
- Process large files in streaming mode for CSV and JSON to avoid loading everything into memory, triggered via --stream flag
- Support custom CSV delimiters via --delimiter flag (comma, tab, pipe, semicolon)
- Support selecting a subset of fields/columns via --fields flag
- Print conversion summary to console: input format, output format, row count, column count, any data loss warnings
- Save the converted output to a file specified by --output flag (default: output.{format})
- If no input file is given, generate a sample dataset with nested objects, arrays, mixed types, and null values in JSON format, then convert it to all other formats
- Handle encoding differences (UTF-8, Latin-1) and BOM markers gracefully

Create a complete Java project for a clean Ubuntu 22.04 machine with only JDK 17+ installed. Include:
- Source code
- pom.xml 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.

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>tmlr.codex_generated.p08</groupId>
  <artifactId>multi-format-data-converter</artifactId>
  <version>1.0.0</version>
  <name>Multi-Format Data Converter</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.release>17</maven.compiler.release>
  </properties>

  <dependencies>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.18.2</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.18.2</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.18.2</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.dataformat</groupId>
      <artifactId>jackson-dataformat-xml</artifactId>
      <version>2.18.2</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.dataformat</groupId>
      <artifactId>jackson-dataformat-yaml</artifactId>
      <version>2.18.2</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.woodstox</groupId>
      <artifactId>woodstox-core</artifactId>
      <version>6.7.0</version>
    </dependency>
    <dependency>
      <groupId>org.yaml</groupId>
      <artifactId>snakeyaml</artifactId>
      <version>2.3</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.13.0</version>
      </plugin>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>3.5.0</version>
        <configuration>
          <mainClass>MultiFormatDataConverter</mainClass>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
README.md
# Multi-Format Data Converter (Java)

Converts between `CSV`, `JSON`, `XML`, and `YAML` with nested flatten/unflatten support, type preservation, field filtering, and stream-mode parsing for CSV/NDJSON.

## Requirements

- Ubuntu 22.04
- JDK 17+
- Maven 3.9+

## Dependencies

- Direct (all pinned):
  - `com.fasterxml.jackson.core:jackson-core:2.18.2`
  - `com.fasterxml.jackson.core:jackson-annotations:2.18.2`
  - `com.fasterxml.jackson.core:jackson-databind:2.18.2`
  - `com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.18.2`
  - `com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.18.2`
  - `com.fasterxml.woodstox:woodstox-core:6.7.0`
  - `org.yaml:snakeyaml:2.3`

## Build

```bash
mvn -q -DskipTests compile
```

## Run

General form:

```bash
mvn -q exec:java -Dexec.args="<input-file> <output-format> [--output file] [--detect] [--stream] [--delimiter comma|tab|pipe|semicolon] [--fields a,b,c]"
```

Examples:

```bash
mvn -q exec:java -Dexec.args="./data.json csv --output out.csv --fields id,name,address.city"
mvn -q exec:java -Dexec.args="./data.csv json --delimiter semicolon --detect"
mvn -q exec:java -Dexec.args="./data.ndjson yaml --stream"
```

No input (sample generation + conversion to all other formats):

```bash
mvn -q exec:java
```
src/main/java/MultiFormatDataConverter.java
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class MultiFormatDataConverter {
    private static final ObjectMapper JSON = new ObjectMapper();
    private static final ObjectMapper YAML = new YAMLMapper();
    private static final XmlMapper XML = new XmlMapper();
    private static final Set<String> FORMATS = Set.of("csv", "json", "xml", "yaml");

    public static void main(String[] args) {
        try {
            ParsedArgs parsed = parseArgs(args);
            if (parsed.positional.isEmpty()) {
                runSampleMode(parsed.options);
                return;
            }

            Path inputPath = Paths.get(parsed.positional.get(0)).toAbsolutePath();
            if (!Files.exists(inputPath)) {
                System.err.println("Input file not found: " + inputPath);
                System.exit(1);
            }

            String outputFormat = "";
            if (parsed.positional.size() >= 2) outputFormat = parsed.positional.get(1).toLowerCase();
            if (outputFormat.isEmpty()) outputFormat = parsed.options.getOrDefault("format", "").toLowerCase();
            if (!FORMATS.contains(outputFormat)) {
                System.err.println("Provide output format as positional or --format (csv|json|xml|yaml).");
                System.exit(1);
            }

            convertSingle(inputPath, outputFormat, parsed.options);
        } catch (Exception e) {
            System.err.println("Conversion failed: " + e.getMessage());
            System.exit(1);
        }
    }

    private record ParsedArgs(Map<String, String> options, List<String> positional) {}

    private static ParsedArgs parseArgs(String[] args) {
        Map<String, String> options = new HashMap<>();
        List<String> positional = new ArrayList<>();
        for (int i = 0; i < args.length; i++) {
            String token = args[i];
            if (token.startsWith("--")) {
                String key = token.substring(2);
                if (i + 1 < args.length && !args[i + 1].startsWith("--")) options.put(key, args[++i]);
                else options.put(key, "true");
            } else {
                positional.add(token);
            }
        }
        return new ParsedArgs(options, positional);
    }

    private static String normalizeDelimiter(String value) {
        if (value == null || value.isBlank() || value.equals(",") || value.equals("comma")) return ",";
        if (value.equals("\\t") || value.equals("tab")) return "\t";
        if (value.equals("|") || value.equals("pipe")) return "|";
        if (value.equals(";") || value.equals("semicolon")) return ";";
        return String.valueOf(value.charAt(0));
    }

    private static String decodeWithFallback(byte[] bytes) {
        CharsetDecoder dec = StandardCharsets.UTF_8.newDecoder()
                .onMalformedInput(CodingErrorAction.REPORT)
                .onUnmappableCharacter(CodingErrorAction.REPORT);
        try {
            return stripBom(dec.decode(ByteBuffer.wrap(bytes)).toString());
        } catch (CharacterCodingException e) {
            return stripBom(new String(bytes, StandardCharsets.ISO_8859_1));
        }
    }

    private static String detectEncoding(byte[] bytes) {
        CharsetDecoder dec = StandardCharsets.UTF_8.newDecoder()
                .onMalformedInput(CodingErrorAction.REPORT)
                .onUnmappableCharacter(CodingErrorAction.REPORT);
        try {
            dec.decode(ByteBuffer.wrap(bytes));
            return "utf-8";
        } catch (CharacterCodingException e) {
            return "latin1";
        }
    }

    private static String stripBom(String text) {
        return text.startsWith("\uFEFF") ? text.substring(1) : text;
    }

    private static String detectInputFormat(Path inputPath, String text, boolean detect) {
        if (!detect) {
            String ext = extension(inputPath);
            if (ext.equals("csv")) return "csv";
            if (ext.equals("json") || ext.equals("jsonl") || ext.equals("ndjson")) return "json";
            if (ext.equals("xml")) return "xml";
            if (ext.equals("yaml") || ext.equals("yml")) return "yaml";
        }
        String trimmed = text.trim();
        if (trimmed.startsWith("{") || trimmed.startsWith("[") || trimmed.lines().allMatch(l -> l.isBlank() || l.trim().startsWith("{")))
            return "json";
        if (trimmed.startsWith("<")) return "xml";
        if (!trimmed.isBlank() && trimmed.split("\\R", 2)[0].contains(":")) return "yaml";
        return "csv";
    }

    private static String extension(Path path) {
        String name = path.getFileName().toString();
        int idx = name.lastIndexOf('.');
        return idx >= 0 ? name.substring(idx + 1).toLowerCase() : "";
    }

    private static Object parsePrimitive(String value) {
        if (value == null) return null;
        String s = value.trim();
        if (s.isEmpty() || s.equalsIgnoreCase("null")) return null;
        if (s.equalsIgnoreCase("true")) return true;
        if (s.equalsIgnoreCase("false")) return false;
        if (s.matches("^[-+]?\\d+$")) return Long.parseLong(s);
        if (s.matches("^[-+]?\\d+\\.\\d+$")) return Double.parseDouble(s);
        return value;
    }

    private static List<String> parseCsvLine(String line, char delimiter) {
        List<String> out = new ArrayList<>();
        StringBuilder current = new StringBuilder();
        boolean inQuotes = false;
        for (int i = 0; i < line.length(); i++) {
            char ch = line.charAt(i);
            if (ch == '"') {
                if (inQuotes && i + 1 < line.length() && line.charAt(i + 1) == '"') {
                    current.append('"');
                    i++;
                } else {
                    inQuotes = !inQuotes;
                }
            } else if (ch == delimiter && !inQuotes) {
                out.add(current.toString());
                current.setLength(0);
            } else {
                current.append(ch);
            }
        }
        out.add(current.toString());
        return out;
    }

    private static String csvEscape(Object value, char delimiter) {
        String s = value == null ? "" : String.valueOf(value);
        if (s.indexOf(delimiter) >= 0 || s.contains("\"") || s.contains("\n")) {
            return "\"" + s.replace("\"", "\"\"") + "\"";
        }
        return s;
    }

    @SuppressWarnings("unchecked")
    private static void flattenObject(Object value, String prefix, Map<String, Object> out) {
        if (value == null) {
            if (!prefix.isEmpty()) out.put(prefix, null);
            return;
        }
        if (value instanceof List<?> list) {
            if (list.isEmpty() && !prefix.isEmpty()) out.put(prefix, Collections.emptyList());
            for (int i = 0; i < list.size(); i++) {
                String key = prefix.isEmpty() ? String.valueOf(i) : prefix + "." + i;
                flattenObject(list.get(i), key, out);
            }
            return;
        }
        if (value instanceof Map<?, ?> map) {
            if (map.isEmpty() && !prefix.isEmpty()) out.put(prefix, Collections.emptyMap());
            for (Map.Entry<?, ?> e : map.entrySet()) {
                String key = String.valueOf(e.getKey());
                String next = prefix.isEmpty() ? key : prefix + "." + key;
                flattenObject(e.getValue(), next, out);
            }
            return;
        }
        if (!prefix.isEmpty()) out.put(prefix, value);
    }

    private static void assignPath(Map<String, Object> root, List<String> parts, Object value) {
        Object current = root;
        for (int i = 0; i < parts.size(); i++) {
            String part = parts.get(i);
            boolean isLast = i == parts.size() - 1;
            boolean isIndex = part.matches("^\\d+$");

            if (isLast) {
                if (current instanceof List<?> list && isIndex) {
                    @SuppressWarnings("unchecked")
                    List<Object> target = (List<Object>) list;
                    int idx = Integer.parseInt(part);
                    while (target.size() <= idx) target.add(null);
                    target.set(idx, value);
                } else if (current instanceof Map<?, ?> map) {
                    @SuppressWarnings("unchecked")
                    Map<String, Object> target = (Map<String, Object>) map;
                    target.put(part, value);
                }
                return;
            }

            boolean nextIsIndex = parts.get(i + 1).matches("^\\d+$");
            if (current instanceof List<?> list && isIndex) {
                @SuppressWarnings("unchecked")
                List<Object> target = (List<Object>) list;
                int idx = Integer.parseInt(part);
                while (target.size() <= idx) target.add(null);
                if (target.get(idx) == null) target.set(idx, nextIsIndex ? new ArrayList<>() : new LinkedHashMap<>());
                current = target.get(idx);
            } else if (current instanceof Map<?, ?> map) {
                @SuppressWarnings("unchecked")
                Map<String, Object> target = (Map<String, Object>) map;
                if (!target.containsKey(part) || target.get(part) == null) {
                    target.put(part, nextIsIndex ? new ArrayList<>() : new LinkedHashMap<>());
                }
                current = target.get(part);
            }
        }
    }

    private static Map<String, Object> unflattenRow(Map<String, Object> flat) {
        Map<String, Object> out = new LinkedHashMap<>();
        for (Map.Entry<String, Object> e : flat.entrySet()) {
            String key = e.getKey();
            if (!key.contains(".")) {
                out.put(key, e.getValue());
            } else {
                assignPath(out, Arrays.asList(key.split("\\.")), e.getValue());
            }
        }
        return out;
    }

    @SuppressWarnings("unchecked")
    private static List<Map<String, Object>> normalizeRows(Object data) {
        if (data instanceof List<?> list) {
            List<Map<String, Object>> out = new ArrayList<>();
            for (Object item : list) {
                if (item instanceof Map<?, ?> map) {
                    Map<String, Object> row = new LinkedHashMap<>();
                    for (Map.Entry<?, ?> e : map.entrySet()) row.put(String.valueOf(e.getKey()), e.getValue());
                    out.add(row);
                } else {
                    Map<String, Object> row = new LinkedHashMap<>();
                    row.put("value", item);
                    out.add(row);
                }
            }
            return out;
        }
        if (data instanceof Map<?, ?> map) {
            Map<String, Object> row = new LinkedHashMap<>();
            for (Map.Entry<?, ?> e : map.entrySet()) row.put(String.valueOf(e.getKey()), e.getValue());
            return new ArrayList<>(List.of(row));
        }
        Map<String, Object> row = new LinkedHashMap<>();
        row.put("value", data);
        return new ArrayList<>(List.of(row));
    }

    private static List<Map<String, Object>> applyFields(List<Map<String, Object>> rows, List<String> fields) {
        if (fields.isEmpty()) return rows;
        List<Map<String, Object>> out = new ArrayList<>();
        for (Map<String, Object> row : rows) {
            Map<String, Object> flat = new LinkedHashMap<>();
            flattenObject(row, "", flat);
            Map<String, Object> selected = new LinkedHashMap<>();
            for (String field : fields) if (flat.containsKey(field)) selected.put(field, flat.get(field));
            out.add(unflattenRow(selected));
        }
        return out;
    }

    private static List<Map<String, Object>> parseInput(Path inputPath, String inputFormat, char delimiter, boolean stream, List<String> warnings) throws IOException {
        if (stream && inputFormat.equals("csv")) return parseCsvStream(inputPath, delimiter);
        if (stream && inputFormat.equals("json")) {
            try {
                return parseJsonStream(inputPath);
            } catch (Exception e) {
                warnings.add("JSON stream mode expects NDJSON; falling back to full parse.");
            }
        }

        String text = decodeWithFallback(Files.readAllBytes(inputPath));
        if (inputFormat.equals("csv")) return parseCsvText(text, delimiter);
        if (inputFormat.equals("json")) return normalizeRows(JSON.readValue(text, Object.class));
        if (inputFormat.equals("yaml")) return normalizeRows(YAML.readValue(text, Object.class));
        if (inputFormat.equals("xml")) {
            Object parsed = XML.readValue(text, Object.class);
            if (parsed instanceof Map<?, ?> map && map.containsKey("root")) {
                Object root = map.get("root");
                if (root instanceof Map<?, ?> rootMap && rootMap.containsKey("item")) return normalizeRows(rootMap.get("item"));
            }
            return normalizeRows(parsed);
        }
        throw new IllegalArgumentException("Unsupported input format: " + inputFormat);
    }

    private static List<Map<String, Object>> parseCsvText(String text, char delimiter) {
        List<String> lines = text.replace("\r\n", "\n").replace("\r", "\n").lines()
                .filter(l -> !l.isBlank()).toList();
        if (lines.isEmpty()) return new ArrayList<>();
        List<String> headers = parseCsvLine(lines.get(0), delimiter);
        List<Map<String, Object>> rows = new ArrayList<>();
        for (int i = 1; i < lines.size(); i++) {
            List<String> values = parseCsvLine(lines.get(i), delimiter);
            Map<String, Object> flat = new LinkedHashMap<>();
            for (int j = 0; j < headers.size(); j++) {
                flat.put(headers.get(j).trim(), parsePrimitive(j < values.size() ? values.get(j) : ""));
            }
            rows.add(unflattenRow(flat));
        }
        return rows;
    }

    private static List<Map<String, Object>> parseCsvStream(Path path, char delimiter) throws IOException {
        try (BufferedReader br = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
            String line;
            List<String> headers = null;
            List<Map<String, Object>> rows = new ArrayList<>();
            while ((line = br.readLine()) != null) {
                line = stripBom(line);
                if (line.isBlank()) continue;
                if (headers == null) {
                    headers = parseCsvLine(line, delimiter);
                    continue;
                }
                List<String> values = parseCsvLine(line, delimiter);
                Map<String, Object> flat = new LinkedHashMap<>();
                for (int j = 0; j < headers.size(); j++) {
                    flat.put(headers.get(j).trim(), parsePrimitive(j < values.size() ? values.get(j) : ""));
                }
                rows.add(unflattenRow(flat));
            }
            return rows;
        }
    }

    private static List<Map<String, Object>> parseJsonStream(Path path) throws IOException {
        try (BufferedReader br = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
            String line;
            List<Map<String, Object>> rows = new ArrayList<>();
            while ((line = br.readLine()) != null) {
                String t = line.trim();
                if (t.isBlank()) continue;
                if (!(t.startsWith("{") && t.endsWith("}"))) {
                    throw new IllegalArgumentException("JSON stream mode expects NDJSON object lines.");
                }
                Map<String, Object> row = JSON.readValue(t, new TypeReference<>() {});
                rows.add(row);
            }
            return rows;
        }
    }

    private static String serializeCsv(List<Map<String, Object>> rows, char delimiter) {
        List<Map<String, Object>> flatRows = new ArrayList<>();
        LinkedHashSet<String> columns = new LinkedHashSet<>();
        for (Map<String, Object> row : rows) {
            Map<String, Object> flat = new LinkedHashMap<>();
            flattenObject(row, "", flat);
            flatRows.add(flat);
            columns.addAll(flat.keySet());
        }
        StringBuilder out = new StringBuilder();
        List<String> headers = new ArrayList<>(columns);
        out.append(String.join(String.valueOf(delimiter), headers)).append("\n");
        for (Map<String, Object> row : flatRows) {
            List<String> vals = new ArrayList<>();
            for (String h : headers) vals.add(csvEscape(row.get(h), delimiter));
            out.append(String.join(String.valueOf(delimiter), vals)).append("\n");
        }
        return out.toString();
    }

    private static String serializeJson(List<Map<String, Object>> rows) throws IOException {
        return JSON.writerWithDefaultPrettyPrinter().writeValueAsString(rows) + "\n";
    }

    private static String serializeYaml(List<Map<String, Object>> rows) throws IOException {
        return YAML.writeValueAsString(rows);
    }

    private static String serializeXml(List<Map<String, Object>> rows) throws IOException {
        Map<String, Object> root = new LinkedHashMap<>();
        root.put("item", rows);
        Map<String, Object> payload = new LinkedHashMap<>();
        payload.put("root", root);
        return XML.writerWithDefaultPrettyPrinter().writeValueAsString(payload);
    }

    private static void writeOutput(Path outputPath, String outputFormat, List<Map<String, Object>> rows, char delimiter) throws IOException {
        String content;
        switch (outputFormat) {
            case "csv" -> content = serializeCsv(rows, delimiter);
            case "json" -> content = serializeJson(rows);
            case "yaml" -> content = serializeYaml(rows);
            case "xml" -> content = serializeXml(rows);
            default -> throw new IllegalArgumentException("Unsupported output format: " + outputFormat);
        }
        Files.writeString(outputPath, content, StandardCharsets.UTF_8);
    }

    private static int countColumns(List<Map<String, Object>> rows) {
        Set<String> cols = new LinkedHashSet<>();
        for (Map<String, Object> row : rows) {
            Map<String, Object> flat = new LinkedHashMap<>();
            flattenObject(row, "", flat);
            cols.addAll(flat.keySet());
        }
        return cols.size();
    }

    private static void printSummary(String inputFormat, String outputFormat, List<Map<String, Object>> rows, List<String> warnings) {
        System.out.println("Conversion Summary");
        System.out.println("==================");
        System.out.println("Input format : " + inputFormat);
        System.out.println("Output format: " + outputFormat);
        System.out.println("Row count    : " + rows.size());
        System.out.println("Column count : " + countColumns(rows));
        System.out.println("Warnings     : " + (warnings.isEmpty() ? "none" : String.join(" | ", warnings)));
    }

    private static List<Map<String, Object>> sampleData() {
        List<Map<String, Object>> rows = new ArrayList<>();
        Map<String, Object> row1 = new LinkedHashMap<>();
        row1.put("id", 1);
        row1.put("name", "Alice");
        row1.put("active", true);
        row1.put("score", 97.5);
        row1.put("address", new LinkedHashMap<>(Map.of("city", "Austin", "zip", "73301")));
        row1.put("tags", List.of("premium", "beta"));
        row1.put("orders", List.of(
                new LinkedHashMap<>(Map.of("id", "o1", "amount", 39.95)),
                new LinkedHashMap<>(Map.of("id", "o2", "amount", 12))
        ));
        row1.put("last_login", null);
        rows.add(row1);

        Map<String, Object> row2 = new LinkedHashMap<>();
        row2.put("id", 2);
        row2.put("name", "Bob");
        row2.put("active", false);
        row2.put("score", 88);
        row2.put("address", new LinkedHashMap<>(Map.of("city", "Berlin", "zip", "10115")));
        row2.put("tags", List.of("standard"));
        row2.put("orders", List.of(new LinkedHashMap<>(Map.of("id", "o3", "amount", 120.1))));
        row2.put("last_login", "2026-01-01T10:00:00Z");
        rows.add(row2);
        return rows;
    }

    private static void runSampleMode(Map<String, String> options) throws IOException {
        List<Map<String, Object>> sample = sampleData();
        Path samplePath = Paths.get("sample_data.json").toAbsolutePath();
        Files.writeString(samplePath, serializeJson(sample), StandardCharsets.UTF_8);
        System.out.println("No input provided. Generated sample dataset: " + samplePath);
        char delimiter = normalizeDelimiter(options.getOrDefault("delimiter", "comma")).charAt(0);
        for (String fmt : List.of("csv", "xml", "yaml")) {
            Path output = Paths.get("output." + fmt).toAbsolutePath();
            writeOutput(output, fmt, sample, delimiter);
            printSummary("json", fmt, sample, new ArrayList<>());
            System.out.println("Output file  : " + output);
            System.out.println();
        }
    }

    private static void convertSingle(Path inputPath, String outputFormat, Map<String, String> options) throws IOException {
        char delimiter = normalizeDelimiter(options.getOrDefault("delimiter", "comma")).charAt(0);
        List<String> warnings = new ArrayList<>();
        byte[] bytes = Files.readAllBytes(inputPath);
        String text = decodeWithFallback(bytes);
        String encoding = detectEncoding(bytes);
        if (!encoding.equals("utf-8")) warnings.add("Input decoded as " + encoding);

        String inputFormat = detectInputFormat(inputPath, text, options.containsKey("detect"));
        if (!FORMATS.contains(inputFormat)) throw new IllegalArgumentException("Unsupported input format: " + inputFormat);

        List<Map<String, Object>> rows = parseInput(inputPath, inputFormat, delimiter, options.containsKey("stream"), warnings);
        List<String> fields = new ArrayList<>();
        if (options.containsKey("fields")) {
            for (String f : options.get("fields").split(",")) {
                String t = f.trim();
                if (!t.isEmpty()) fields.add(t);
            }
        }
        rows = applyFields(rows, fields);
        if (outputFormat.equals("csv")) warnings.add("Nested objects/arrays are flattened to dot-notation columns.");

        Path outputPath = options.containsKey("output")
                ? Paths.get(options.get("output")).toAbsolutePath()
                : Paths.get("output." + outputFormat).toAbsolutePath();
        writeOutput(outputPath, outputFormat, rows, delimiter);
        printSummary(inputFormat, outputFormat, rows, warnings);
        System.out.println("Output file  : " + outputPath);
    }
}