From 8df048fc03410d94791a19cc9c59297d80ee193a Mon Sep 17 00:00:00 2001
From: Simone Carletti <5387+weppos@users.noreply.github.com>
Date: Tue, 31 Mar 2026 01:08:28 +0200
Subject: [PATCH] fix: Pretty print elements with intrinsic values on a single
line
Elements using the empty-string CodingKey ("") for intrinsic text
content were being pretty-printed with the value on a separate line:
120000.0
This happened because the pretty printer only checked containsTextNodes,
which isn't set for intrinsic values encoded via keyed containers. Add a
check for elements whose children are all inline (key is empty) and
don't wrap named child elements, treating them the same as text nodes.
---
.../Auxiliaries/XMLCoderElement.swift | 5 +-
Tests/XMLCoderTests/PrettyPrintTest.swift | 102 ++++++++++++++++++
2 files changed, 106 insertions(+), 1 deletion(-)
diff --git a/Sources/XMLCoder/Auxiliaries/XMLCoderElement.swift b/Sources/XMLCoder/Auxiliaries/XMLCoderElement.swift
index 4cf61d91..1b1d8a0f 100644
--- a/Sources/XMLCoder/Auxiliaries/XMLCoderElement.swift
+++ b/Sources/XMLCoder/Auxiliaries/XMLCoderElement.swift
@@ -314,7 +314,10 @@ struct XMLCoderElement: Equatable, Sendable {
}
if !elements.isEmpty || formatting.contains(.noEmptyElements) {
- let prettyPrintElements = prettyPrinted && !containsTextNodes
+ let hasOnlyIntrinsicContent = elements.allSatisfy { element in
+ element.key.isEmpty && !element.elements.contains { !$0.key.isEmpty }
+ }
+ let prettyPrintElements = prettyPrinted && !containsTextNodes && !hasOnlyIntrinsicContent
if !key.isEmpty {
string += prettyPrintElements ? ">\n" : ">"
}
diff --git a/Tests/XMLCoderTests/PrettyPrintTest.swift b/Tests/XMLCoderTests/PrettyPrintTest.swift
index 89f05fa7..8ad412c9 100644
--- a/Tests/XMLCoderTests/PrettyPrintTest.swift
+++ b/Tests/XMLCoderTests/PrettyPrintTest.swift
@@ -14,6 +14,58 @@ private struct NestedContainer: Encodable {
let values: [String]
}
+/// Element with an attribute and intrinsic text value (empty-string CodingKey).
+private struct Measurement: Codable, Equatable {
+ @Attribute var ref: String?
+ var value: Double
+
+ enum CodingKeys: String, CodingKey {
+ case ref
+ case value = ""
+ }
+
+ init(value: Double, ref: String? = nil) {
+ self._ref = Attribute(ref)
+ self.value = value
+ }
+
+ init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+ _ref = try container.decodeIfPresent(Attribute.self, forKey: .ref) ?? Attribute(nil)
+ value = try container.decode(Double.self, forKey: .value)
+ }
+}
+
+private struct Sample: Codable, Equatable {
+ var depth: Double
+ var readings: [Measurement]
+
+ enum CodingKeys: String, CodingKey {
+ case depth
+ case readings = "measurement"
+ }
+
+ init(depth: Double, readings: [Measurement]) {
+ self.depth = depth
+ self.readings = readings
+ }
+
+ init(from decoder: Decoder) throws {
+ let container = try decoder.container(keyedBy: CodingKeys.self)
+ depth = try container.decode(Double.self, forKey: .depth)
+ readings = try container.decodeIfPresent([Measurement].self, forKey: .readings) ?? []
+ }
+
+ func encode(to encoder: Encoder) throws {
+ var container = encoder.container(keyedBy: CodingKeys.self)
+ try container.encode(depth, forKey: .depth)
+ for reading in readings {
+ let enc = container.superEncoder(forKey: .readings)
+ try reading.encode(to: enc)
+ }
+ }
+}
+
final class PrettyPrintTest: XCTestCase {
private let testContainer = TopContainer(nested: NestedContainer(values: ["foor", "bar"]))
@@ -56,6 +108,56 @@ final class PrettyPrintTest: XCTestCase {
)
}
+ func testIntrinsicValueWithoutAttribute() throws {
+ let encoder = XMLEncoder()
+ encoder.outputFormatting = [.prettyPrinted]
+ encoder.prettyPrintIndentation = .spaces(4)
+
+ let sample = Sample(depth: 6.0, readings: [
+ Measurement(value: 118000.0),
+ ])
+ let encoded = try encoder.encode(sample, withRootKey: "sample")
+
+ XCTAssertEqual(
+ String(data: encoded, encoding: .utf8)!,
+ """
+
+ 6.0
+ 118000.0
+
+ """
+ )
+
+ let decoded = try XMLDecoder().decode(Sample.self, from: encoded)
+ XCTAssertEqual(decoded, sample)
+ }
+
+ func testIntrinsicValueWithAttribute() throws {
+ let encoder = XMLEncoder()
+ encoder.outputFormatting = [.prettyPrinted]
+ encoder.prettyPrintIndentation = .spaces(4)
+
+ let sample = Sample(depth: 30.0, readings: [
+ Measurement(value: 120000.0, ref: "sensor-1"),
+ Measurement(value: 122000.0, ref: "sensor-2"),
+ ])
+ let encoded = try encoder.encode(sample, withRootKey: "sample")
+
+ XCTAssertEqual(
+ String(data: encoded, encoding: .utf8)!,
+ """
+
+ 30.0
+ 120000.0
+ 122000.0
+
+ """
+ )
+
+ let decoded = try XMLDecoder().decode(Sample.self, from: encoded)
+ XCTAssertEqual(decoded, sample)
+ }
+
func testTabs() throws {
let encoder = XMLEncoder()
encoder.outputFormatting = [.prettyPrinted]