Repository navigation
SwiftXMLCoder 1.0.0
[1.0.0] — 2026-03-16
Added (Epic H — Pre-Release API Completeness)
- H.1 —
userInfoonXMLEncoderandXMLDecoder:XMLEncoder.ConfigurationandXMLDecoder.Configurationnow acceptuserInfo: [CodingUserInfoKey: Any](default[:]). Both internal types_XMLTreeEncoderand_XMLTreeDecodernow returnoptions.userInfofrom theirEncoder/Decoderprotocol property, enabling context injection into customEncodable/Decodableimplementations. BothConfigurationstructs changed fromSendableto@unchecked Sendable(required by[CodingUserInfoKey: Any]; same approach asFoundation.JSONEncoder). - H.2a —
NilEncodingStrategyfix for synthesised optionals:_XMLKeyedEncodingContainernow overrides all 15 concreteencodeIfPresent(_:forKey:)overloads (Bool, String, Double, Float, Int, Int8, Int16, Int32, Int64, UInt, UInt8, UInt16, UInt32, UInt64, and genericT) plus the genericencodeIfPresent<T: Encodable>. Previously, Swift's synthesisedencode(to:)bypassedencodeNil(forKey:)entirely by calling the concreteencodeIfPresentoverloads, makingNilEncodingStrategyineffective for compiler-synthesisedEncodableconformances. All overloads delegate to_encodeIfPresentwhich routes nil throughencodeNil(forKey:). - H.3 —
XMLKeyTransformStrategy: NewXMLKeyTransformStrategyenum (useDefaultKeys,convertToSnakeCase,convertToKebabCase,capitalized,uppercased,lowercased,custom(@Sendable (String) -> String)). Added toXMLEncoder.ConfigurationandXMLDecoder.Configuration(default.useDefaultKeys). Encoder applies the transform tokey.stringValuebefore creating child element/attribute names; decoder applies the same transform tokey.stringValuebefore performing lookups in the tree (transform-then-match strategy — no inverse required). Attributes (@XMLAttribute) respect the same transform. New fileSources/SwiftXMLCoder/XMLKeyTransformStrategy.swift. - H.4a —
StringEncodingStrategy(global CDATA): NewXMLEncoder.StringEncodingStrategyenum (.textdefault,.cdata) added toXMLEncoder.Configuration. When.cdata, allStringvalues are wrapped in CDATA sections (<![CDATA[...]]>)._XMLTreeElementBoxgainsappendCDATA(_:)and a.cdata(String)case on_XMLTreeContentBox, mapped toXMLTreeNode.cdatainmakeElement(). The decode path already handled CDATA transparently viaXMLTreeParser. - H.4b —
@XMLCDATAmacro (per-field CDATA): New@XMLCDATA()peer macro;XMLStringCodingOverrideProviderprotocol;XMLStringEncodingHintenum (.text,.cdata).@XMLCodablenow synthesisesXMLStringCodingOverrideProviderconformance (xmlPropertyStringHints) when at least one property is annotated@XMLCDATA. The encoder resolves: per-property hint → globalstringEncodingStrategy→.text. Attributes annotated with@XMLCDATAcompile but the hint is silently ignored (CDATA is not valid in attributes). New files:Sources/SwiftXMLCoder/XMLStringEncodingHint.swift,Sources/SwiftXMLCoderMacroImplementation/XMLCDATAMacro.swift. - H.5 — Whitespace policy discoverability:
XMLDecodergains apublic var whitespacePolicy: XMLTreeParser.WhitespaceTextNodePolicycomputed property as a convenience accessor forconfiguration.parserConfiguration.whitespaceTextNodePolicy(previously buried 2 levels deep). - H.6a —
expandEmptyElementsinXMLTreeWriter:XMLTreeWriter.ConfigurationgainsexpandEmptyElements: Bool(defaultfalse). Whentrue, child-less elements are emitted as<tag></tag>instead of<tag/>by injecting an empty text node before serialisation. Implemented withoutXML_SAVE_NO_EMPTY(a libxml2 global flag); instead, the writer injects an empty text node per element — libxml2 omits self-closing only when an element has at least one child. - H.6b —
@XMLExpandEmptymacro (per-field expand-empty): New@XMLExpandEmpty()peer macro;XMLExpandEmptyProviderprotocol (xmlPropertyExpandEmptyKeys: Set<String>).@XMLCodablenow synthesisesXMLExpandEmptyProviderwhen at least one property is annotated@XMLExpandEmpty. The encoder injects an empty text node into the child element box after encoding, if the box has no content and the key is inxmlPropertyExpandEmptyKeys. The empty text node is dropped by the parser's defaultwhitespaceTextNodePolicy: .dropWhitespaceOnly, so decoded values remain semantically unchanged. New files:Sources/SwiftXMLCoder/XMLExpandEmptyProvider.swift,Sources/SwiftXMLCoderMacroImplementation/XMLExpandEmptyMacro.swift. @XMLCodablemacro declaration updated:conformanceslist now includesXMLFieldCodingOverrideProvider,XMLDateCodingOverrideProvider,XMLStringCodingOverrideProvider,XMLExpandEmptyProvider;nameslist now includes all four synthesised property names.- 42 new tests:
XMLKeyTransformStrategyTests(12),XMLCDATAMacroIntegrationTests(6),XMLExpandEmptyMacroIntegrationTests(5),XMLEncoderTestsH.1 (2) / H.2a (2) / H.4a (4),XMLDecoderTestsH.1 (2),XMLTreeParserWriterTestsH.6a (4).
Added
-
XMLValidationPolicy: build-time and runtime configurable validation policy. Default is.lenient(no validation);.strictenables element-name and XSD temporal validation. Set at build time via-DSWIFT_XML_CODER_STRICT_VALIDATION; override per-instance viaXMLEncoder.Configuration.validationPolicy/XMLDecoder.Configuration.validationPolicy. -
XSD temporal value types:
XMLGYear,XMLGYearMonth,XMLGMonth,XMLGDay,XMLGMonthDay,XMLTime,XMLDuration— allSendable,Equatable,Hashable,Codable(encode/decode as XSD lexical strings). Each type with aFoundation.Datebridge where meaningful. -
XMLTimezoneOffset: value type for XSD timezone offsets (Z,±HH:MM);public static let utc. -
TimeZone.utc: convenience public extension. -
DateEncodingStrategynew cases:.xsdDate(timeZone:),.xsdTime(timeZone:),.xsdGYear(timeZone:),.xsdGYearMonth(timeZone:),.xsdGMonth(timeZone:),.xsdGDay(timeZone:),.xsdGMonthDay(timeZone:)— encodeFoundation.Dateas each XSD partial-date form. -
DateDecodingStrategynew cases:.xsdDate,.xsdTime,.xsdGYear,.xsdGYearMonth,.xsdGMonth,.xsdGDay,.xsdGMonthDay— decode XSD partial-date lexical strings intoFoundation.Date. -
_XMLTemporalFoundationSupport.formatXSDDate(_:timeZone:)andparseXSDDate(_:)helpers forxs:date↔Foundation.Dateconversion. -
61 new tests in
XMLTemporalTypesTestscovering parsing, roundtrip, invalid input,Foundation.Datebridges, andXMLValidationPolicybuild-time/runtime behavior. -
XMLDocument.ParsingConfiguration.untrusted()static factory that explicitly enforces all libxml2 hardening flags (.forbidNetwork, DTD forbidden, entity references preserved, blank text nodes trimmed). Intended for use withXMLTreeParser.Limits.untrustedInputDefault()for full defence-in-depth against malicious XML input. -
3 new tests in
XMLTreeHardeningTestscoveringuntrusted()policy assertions, rejection of a deeply-nested XML bomb, and rejection of an oversized text node. -
XMLRootNameResolver.explicitRootElementName(from:validationPolicy:)andimplicitRootElementName(for:validationPolicy:)now accept avalidationPolicyparameter. In.strictmode, arootElementNameorXMLRootNode.xmlRootElementNamethat requires sanitization fails with[XML6_6_ROOT_NAME_INVALID]instead of silently correcting. -
_XMLEncoderOptions.initnow throws[XML6_6_ITEM_NAME_INVALID]in.strictmode whenitemElementNamerequires sanitization. In.lenientmode (default) the name is still sanitized silently. -
8 new tests in
XMLEncoderTests(D.1 matrix): strict rejection ofrootElementNamewith space, digit-prefix, invalidXMLRootNodename, anditemElementNamewith space; lenient sanitization pass for root and item names; valid-name no-throw assertions. -
Tests/.swiftlint.yml: raisedtype_body_lengthwarning threshold to 600 lines for test files (test classes grow with coverage matrix). -
New test target
SwiftXMLCoderMacroTests(Swift 5.9+ manifests): usesSwiftSyntaxMacrosTestSupport.assertMacroExpansionto verify macro diagnostic IDs and expansion output. Covers:@XMLCodableonenum→XML8A_INVALID_DECLerror;@XMLCodableonactor→ same error;@XMLCodableon struct/class → correctxmlFieldNodeKindsexpansion;@XMLAttribute/@XMLChildas pure peer markers (no generated peers). -
@XMLDateFormat(_ hint: XMLDateFormatHint)macro (Swift 5.9+ only): per-property date format annotation for stored properties of typeDateorDate?. Apply inside a type annotated with@XMLCodableto override the globaldateEncodingStrategy/dateDecodingStrategyfor that property. -
XMLDateFormatHintenum:Sendable,Equatable,Hashable,Codable. Covers all XSD date strategies:.xsdDate,.xsdTime,.xsdDateTime,.xsdGYear,.xsdGYearMonth,.xsdGMonth,.xsdGDay,.xsdGMonthDay,.xsdDateWithTimezone(identifier:),.xsdTimeWithTimezone(identifier:),.secondsSince1970,.millisecondsSince1970. Converts toXMLEncoder.DateEncodingStrategyandXMLDecoder.DateDecodingStrategyvia.encodingStrategyand.decodingStrategycomputed properties. -
XMLDateCodingOverrideProviderprotocol: synthesised by@XMLCodablewhen any property is annotated with@XMLDateFormat. The staticxmlPropertyDateHints: [String: XMLDateFormatHint]dictionary is consulted by encoder and decoder before the global strategy. -
@XMLCodablenow synthesises a second extension (XMLDateCodingOverrideProvider) when at least one@XMLDateFormatannotation is present. ThexmlPropertyDateHintsdictionary maps property names to their hints verbatim. -
XMLEncoderandXMLDecodernow apply per-property date hints fromXMLDateCodingOverrideProviderbefore the instance-level strategy, for both top-level types and all nested types. -
14 new integration tests in
XMLDateFormatMacroIntegrationTests: hint → strategy conversions, per-property encode override, per-property decode override, roundtrip, global fallback, Codable roundtrip for allXMLDateFormatHintcases. -
4 new macro expansion tests in
XMLMacroDiagnosticsTests:@XMLDateFormatas pure peer marker;@XMLCodablewith@XMLDateFormat→xmlPropertyDateHintsextension; mixed@XMLAttribute/@XMLDateFormat; complex parameterised hint passed verbatim. -
XMLCapturingLogHandlerinSwiftXMLCoderTestSupport: thread-safeLogHandlerimplementation that captures all log entries for assertion in tests. Providesentries,entries(at:),entries(containing:),hasEntry(at:containing:),hasEntry(at:withMetadataKey:), andreset()helpers. Enables white-box verification that log messages are emitted at the correct levels with the correct metadata. -
XMLEncoder.ConfigurationandXMLDecoder.Configurationnow accept alogger: Loggerparameter (defaultLogger(label: "SwiftXMLCoder")). The logger is propagated through_XMLEncoderOptionsand_XMLDecoderOptionsto all call sites. -
XMLTreeParser.Configurationnow accepts alogger: Loggerparameter; the logger is passed toXMLDocument.initfor libxml2 diagnostics and used for structured parse-phase logging. -
XMLEncoderemits.debugat encode start (type,rootElement) and completion (rootElement,childCount). Emits.warningwhenrootElementNameorXMLRootNode.xmlRootElementNameis silently sanitized in lenient mode. Emits.debugwhen root element name is derived from type name. -
XMLDecoderemits.debugat decode start (type,rootElement) and completion (rootElement,childCount). Emits.errorbefore throwingXML6_5_ROOT_MISMATCH(withexpected,found,typemetadata). -
XMLTreeParseremits.debugon parse start and completion (withnodeCount). Emits.warningwhen any limit is exceeded (withcode,context,actual,limitmetadata). Emits.warningonce when node count or element depth first reaches 80% of the configured cap — subsequent nodes at the same level do not re-emit. -
Encoder and decoder emit
.trace(not.debug) when a per-property date hint fromXMLDateCodingOverrideProvideroverrides the global strategy (withfieldandhintmetadata). -
20 new integration tests in
XMLStructuredLoggingTestscovering encoder/decoder/parser debug lifecycle entries with metadata assertions, root name sanitization warnings, root mismatch error log, per-property hint trace entries, limit exceeded/approaching warnings (including once-only guarantee), andXMLCapturingLogHandlerhelpers. -
iOS 15+, tvOS 15+, watchOS 8+ platform support (Epic G.1 — Track 1): libxml2 resolves via Xcode SDK sysroot;
pkgConfigsilently no-ops on device/simulator SDKs. No source changes required. CI addsios-simulatorbuild-only job (arm64-apple-ios15.0-simulator). -
Platformsbadge and Requirements table inREADME.mdupdated to include iOS, tvOS, watchOS. -
DocC
Articles/Compatibility.mddocuments new Apple platform targets and CI gate policy.
Changed
- XML field-name validation (error code
XML6_6_FIELD_NAME_INVALID) is now gated byXMLValidationPolicy.validateElementNames. Existing tests updated to usevalidationPolicy: .strictto preserve their intent. In lenient mode (default) invalid field names are silently passed through to libxml2.
Breaking Changes
- Renamed
XMLElement<Value>property wrapper toXMLChild<Value>. The old name is retained as a@available(*, deprecated, renamed: "XMLChild")typealias for source compatibility. - Renamed
@XMLElementmacro to@XMLChild. The old macro name is kept as a deprecated alias pointing to the same implementation (XMLChildMacro). - Removed
preserveWhitespaceTextNodes: Boolparameter fromXMLTreeParser.Configuration.initandConfiguration.untrustedInputProfile. UsewhitespaceTextNodePolicy: .preserveinstead. XMLCanonicalizationError.codenow returnsXMLCanonicalizationErrorCodeinstead ofString. Update callers that compare.codeto raw string literals to use the typed constant (e.g..code == .transformFailed).
Added
XMLNamespaceconforms toEquatableandHashable.XMLParsingErrorconforms toEquatable. Two.othervalues are never equal (existential payload cannot be compared structurally — documented in the case's doc comment).makeXMLSafeNamenow strips namespace prefixes from qualified names (e.g."soap:Envelope"→"Envelope"instead of"soap_Envelope"). Regression test added.- Regression test
test_encodeTree_itemElementName_namespacePrefixed_stripsPrefix. - DocC catalog (
Sources/SwiftXMLCoder/SwiftXMLCoder.docc/): landing page + 8 articles (GettingStarted, FieldMapping, Namespaces, Canonicalization, XPath, Security, Compatibility, TestSupport). README.md: installation instructions (all 3 SPM product variants), quick-start code examples, feature matrix, and links to DocC articles.- Inline
///doc comments on all previously undocumented public types and members:XMLNode,XMLNamespaceDeclaration,XMLNormalizationOptions,XMLTemporalCodingtypes (XMLDateFormatterDescriptor,XMLDateCodingContext,XMLDateEncodingClosure,XMLDateDecodingClosure),XMLCanonicalizationErrorCode(all static constants),XMLNamespace,XMLQualifiedName,XMLTreeNode(all cases),XMLTreeAttribute,XMLCanonicalizationError,XMLCanonicalizationStage,XMLCanonicalView,XMLIdentityTransform,XMLDefaultCanonicalizer,XMLNamespaceResolver,XMLNamespaceValidator,XMLDocument(createElement,appendChild,serializedData, XPath methods). - GitHub Actions CI workflow (
.github/workflows/ci.yml): matrix build and test across Linux (Swift 5.6, 5.9, 5.10, 6.0, 6.1) and macOS 15 (Swift 6.1 / Xcode 16.2). Coverage report generated viallvm-covand uploaded as artifact on pushes tomain. - GitHub Actions SwiftLint workflow (
.github/workflows/lint.yml): runsrealm/SwiftLint@v0.57.1with PR review annotations on every push and pull request tomain. - GitHub Actions DocC workflow (
.github/workflows/docc.yml): builds documentation for theSwiftXMLCodertarget on macOS; will enforce--warnings-as-errorsonce the DocC catalog is added in Epic B. Tests/.swiftlint.yml: nested SwiftLint config relaxingidentifier_name(min 1) andtype_name(min 1) for test files, and demotingforce_unwrappingfrom error to warning in tests. Source code remains at full strictness.