Summary
Convert all XML marshalling tests from JAXB (JAXBContext, Marshaller, Unmarshaller) to Jackson 3 XmlMapper to match production code implementation.
Background
Production code uses Jackson 3 XmlMapper with JAXB annotations (hybrid approach):
Serialization engine: tools.jackson.dataformat:jackson-dataformat-xml:3.0.3
Annotation support: tools.jackson.module:jackson-module-jakarta-xmlbind-annotations:3.0.3
Bridge: JakartaXmlBindAnnotationIntrospector processes JAXB annotations
Test code was still using pure JAXB, creating a mismatch between test and production environments.
Progress Status
✅ Completed (Steps 1-2)
Step 1: TimeConfigurationDtoTest ✅
File: openespi-common/src/test/java/org/greenbuttonalliance/espi/common/dto/usage/TimeConfigurationDtoTest.java
Changes:
✅ Replaced JAXB imports with Jackson 3 imports
✅ Updated setUp() to create XmlMapper with JAXB annotation support
✅ Converted all 11 test methods to use xmlMapper.writeValueAsString() and xmlMapper.readValue()
✅ Changed assertions from JUnit to AssertJ
✅ Fixed namespace assertions (Jackson 3 uses default namespace, not prefixed espi:)
✅ Fixed attribute assertion (mRID instead of id)
Results: All 11 tests passing ✅
Related Fix: Added @XmlTransient to 6 utility methods in TimeConfigurationDto.java:
getTzOffsetInHours()
getDstOffsetInHours()
getEffectiveOffset()
getEffectiveOffsetInHours()
hasDstRules()
isDstActive()
Reason: TimeConfigurationDto is a POJO with @XmlAccessorType(XmlAccessType.PROPERTY) which serializes ALL public getters. Utility methods need @XmlTransient to exclude them from XML serialization.
Step 2: Jackson3XmlMarshallingTest ✅
File: openespi-common/src/test/java/org/greenbuttonalliance/espi/common/Jackson3XmlMarshallingTest.java (renamed from SimpleXmlMarshallingTest.java)
Changes:
✅ Removed @Disabled annotation
✅ Renamed class from SimpleXmlMarshallingTest to Jackson3XmlMarshallingTest
✅ Replaced JAXB imports with Jackson 3 imports
✅ Updated setUp() to create XmlMapper
✅ Converted all 7 test methods to use Jackson 3
✅ Changed assertions from JUnit to AssertJ
✅ Added pattern matching for namespace attributes (xmlns="")
✅ Fixed XML escaping assertions (Jackson 3 escapes < but not > in quoted strings)
✅ Deleted old SimpleXmlMarshallingTest.java file
Results: All 7 tests passing ✅
Related Fix: Added @XmlTransient to 4 utility methods in UsagePointDto.java:
generateSelfHref()
generateUpHref()
getMeterReadingCount()
usageSummaryCount()
Reason: Same issue as TimeConfigurationDto - POJO with @XmlAccessorType(XmlAccessType.PROPERTY) serializes all public getters.
⏳ Remaining Work (Steps 3-7)
Step 3: Update XmlDebugTest ⏳
File: openespi-common/src/test/java/org/greenbuttonalliance/espi/common/XmlDebugTest.java
Required Changes:
Replace JAXB with Jackson 3
Keep debug output but add assertions
Same pattern as TimeConfigurationDtoTest
Step 4: Fix Test Data UUIDs ⏳
File: openespi-common/src/test/java/org/greenbuttonalliance/espi/common/service/impl/DtoExportServiceImplTest.java
Issue: Test entity UUIDs use Version-4 (random) instead of Version-5 (deterministic)
Example (line 118):
UsagePointEntity usagePointEntity = new UsagePointEntity ();
usagePointEntity .setId (UUID .fromString ("48C2A019-5598-4E16-B0F9-49E4FF27F5FB" ));
// ↑ Version-4 (should be 5)
Fix:
usagePointEntity .setId (UUID .fromString ("48C2A019-5598-5E16-B0F9-49E4FF27F5FB" ));
// ↑ Version-5
ESPI Requirement: Version-5 UUIDs are deterministic (based on namespace + name using SHA-1 hash). Format: xxxxxxxx-xxxx-5xxx-xxxx-xxxxxxxxxxxx
Files to Update:
UsagePoint UUID (line 118)
MeterReading UUID (if present)
ReadingType UUID (if present)
IntervalBlock UUID (if present)
Step 5: Add Comprehensive Assertions to DtoExportServiceImplTest ⏳
File: openespi-common/src/test/java/org/greenbuttonalliance/espi/common/service/impl/DtoExportServiceImplTest.java
Current Issue: Test only prints XML output with System.out.println() - no validation
Required Assertions:
XML structure validation (feed, entry, content elements)
Atom metadata validation (id, title, published, updated)
ESPI namespace validation
Round-trip marshalling/unmarshalling tests
XSD schema validation tests
Version-5 UUID validation (mark @Disabled until DtoExportServiceImpl fixed - see issue Convert XML marshalling tests from JAXB to Jackson 3 XmlMapper #62 )
Link validation (mark @Disabled until DtoExportServiceImpl fixed - see issue Convert XML marshalling tests from JAXB to Jackson 3 XmlMapper #62 )
See: JACKSON3_XML_MARSHALLING_TEST_PLAN.md for detailed assertion examples
Step 6: Create GitHub Issue for DtoExportServiceImpl Enhancement ⏳
DtoExportServiceImpl Issues to Document:
Version-4 UUIDs instead of Version-5:
createAtomEntry() uses UUID.randomUUID() (Version-4 random)
Should use entity.getId() (Version-5 from database)
Missing Atom metadata extraction:
<title> is empty - should use entity.getDescription()
<link> elements are NULL - should extract from entity.selfLink, upLink, relatedLinks
Correct behavior:
<published> and <updated> use NOW (correct)
Reference: JACKSON3_XML_MARSHALLING_TEST_PLAN.md Issue #1 (lines 21-67)
Step 7: Update Documentation and Create PR ⏳
Files to Update:
✅ JACKSON3_XML_MARSHALLING_TEST_PLAN.md (already exists)
Update plan with completed steps
Add this issue number to references
PR Creation:
Branch: feature/jackson3-xml-marshalling-tests
Commit changes with descriptive message
Reference this issue in PR description
Include test results summary
Technical Details
Jackson 3 XmlMapper Configuration
All tests use this standard configuration:
@ BeforeEach
void setUp () {
AnnotationIntrospector intr = XmlAnnotationIntrospector .Pair .instance (
new JakartaXmlBindAnnotationIntrospector (),
new JacksonAnnotationIntrospector ()
);
xmlMapper = XmlMapper .xmlBuilder ()
.annotationIntrospector (intr )
.addModule (new JakartaXmlBindAnnotationModule ()
.setNonNillableInclusion (JsonInclude .Include .NON_EMPTY ))
.enable (SerializationFeature .INDENT_OUTPUT )
.enable (DateTimeFeature .WRITE_DATES_WITH_ZONE_ID )
.disable (XmlWriteFeature .WRITE_NULLS_AS_XSI_NIL )
.defaultDateFormat (new StdDateFormat ())
.build ();
}
Key Differences: Jackson 3 vs JAXB
Namespace Handling:
JAXB: Uses prefixed namespaces (<espi:tzOffset>)
Jackson 3: Uses default namespace (<tzOffset> with xmlns="..." on root)
Both are valid XML
Marshalling API:
JAXB: marshaller.marshal(object, writer)
Jackson 3: xmlMapper.writeValueAsString(object)
Unmarshalling API:
JAXB: (Type) unmarshaller.unmarshal(reader)
Jackson 3: xmlMapper.readValue(xml, Type.class)
Assertions:
Old: JUnit assertions (assertTrue, assertEquals, assertArrayEquals)
New: AssertJ assertions (assertThat().contains(), isEqualTo(), isNotNull())
POJO DTO Issues Found
Problem: TimeConfigurationDto and UsagePointDto are POJOs (not records) with @XmlAccessorType(XmlAccessType.PROPERTY)
Impact: ALL public getters are serialized, including utility methods
Temporary Fix: Add @XmlTransient to utility method getters
Long-term Solution: Convert to records with @XmlAccessorType(XmlAccessType.FIELD) (see issue #61 )
Why Records Are Better:
FIELD access only serializes record components
Utility methods don't need @XmlTransient
Immutable by default
Less boilerplate
Template: See ReadingTypeDto.java for record pattern
Test Results Summary
Total Tests Converted: 18 tests
✅ TimeConfigurationDtoTest: 11 tests passing
✅ Jackson3XmlMarshallingTest: 7 tests passing
Files Modified:
✅ TimeConfigurationDtoTest.java - Converted to Jackson 3
✅ TimeConfigurationDto.java - Added @XmlTransient to utility methods
✅ Jackson3XmlMarshallingTest.java - Created (renamed from SimpleXmlMarshallingTest)
✅ UsagePointDto.java - Added @XmlTransient to utility methods
✅ SimpleXmlMarshallingTest.java - Deleted (replaced by Jackson3XmlMarshallingTest)
Remaining Files:
⏳ XmlDebugTest.java - Needs conversion
⏳ DtoExportServiceImplTest.java - Needs UUID fixes and assertions
Dependencies
Related Issues:
Related Files:
JACKSON3_XML_MARSHALLING_TEST_PLAN.md - Comprehensive test conversion plan
DTO_APPROACH_COMPARISON.md - Jackson 3 + JAXB decision documentation
ReadingTypeDto.java - Record pattern template
Success Criteria
✅ All XML marshalling tests use Jackson 3 XmlMapper (not JAXB)
⏳ TimeConfigurationDtoTest updated and passing (11 tests) ✅
⏳ Jackson3XmlMarshallingTest enabled and passing (7 tests) ✅
⏳ XmlDebugTest updated and passing
⏳ All test entity UUIDs converted to Version-5
⏳ DtoExportServiceImplTest has comprehensive assertions
⏳ All tests validate Jackson 3 processes JAXB annotations correctly
⏳ XSD validation tests confirm ESPI 4.0 schema compliance
⏳ Round-trip marshalling tests confirm data integrity
⏳ GitHub issue created for DtoExportServiceImpl enhancement
⏳ Documentation updated and PR created
Branch: feature/jackson3-xml-marshalling-tests
Assignee: @donal (or appropriate team member)
Labels: testing, jackson, xml-marshalling, espi-compliance
Milestone: Spring Boot 4.0 + Java 25 Migration
Author: Claude Sonnet 4.5
Date: 2026-01-05
Progress: Steps 1-2 complete (18/18 tests passing), Steps 3-7 remaining
Summary
Convert all XML marshalling tests from JAXB (
JAXBContext,Marshaller,Unmarshaller) to Jackson 3XmlMapperto match production code implementation.Background
Production code uses Jackson 3 XmlMapper with JAXB annotations (hybrid approach):
tools.jackson.dataformat:jackson-dataformat-xml:3.0.3tools.jackson.module:jackson-module-jakarta-xmlbind-annotations:3.0.3JakartaXmlBindAnnotationIntrospectorprocesses JAXB annotationsTest code was still using pure JAXB, creating a mismatch between test and production environments.
Progress Status
✅ Completed (Steps 1-2)
Step 1: TimeConfigurationDtoTest ✅
File:
openespi-common/src/test/java/org/greenbuttonalliance/espi/common/dto/usage/TimeConfigurationDtoTest.javaChanges:
setUp()to createXmlMapperwith JAXB annotation supportxmlMapper.writeValueAsString()andxmlMapper.readValue()espi:)mRIDinstead ofid)Results: All 11 tests passing ✅
Related Fix: Added
@XmlTransientto 6 utility methods inTimeConfigurationDto.java:getTzOffsetInHours()getDstOffsetInHours()getEffectiveOffset()getEffectiveOffsetInHours()hasDstRules()isDstActive()Reason: TimeConfigurationDto is a POJO with
@XmlAccessorType(XmlAccessType.PROPERTY)which serializes ALL public getters. Utility methods need@XmlTransientto exclude them from XML serialization.Step 2: Jackson3XmlMarshallingTest ✅
File:
openespi-common/src/test/java/org/greenbuttonalliance/espi/common/Jackson3XmlMarshallingTest.java(renamed fromSimpleXmlMarshallingTest.java)Changes:
@DisabledannotationSimpleXmlMarshallingTesttoJackson3XmlMarshallingTestsetUp()to createXmlMapperxmlns="")<but not>in quoted strings)SimpleXmlMarshallingTest.javafileResults: All 7 tests passing ✅
Related Fix: Added
@XmlTransientto 4 utility methods inUsagePointDto.java:generateSelfHref()generateUpHref()getMeterReadingCount()usageSummaryCount()Reason: Same issue as TimeConfigurationDto - POJO with
@XmlAccessorType(XmlAccessType.PROPERTY)serializes all public getters.⏳ Remaining Work (Steps 3-7)
Step 3: Update XmlDebugTest ⏳
File:
openespi-common/src/test/java/org/greenbuttonalliance/espi/common/XmlDebugTest.javaRequired Changes:
Step 4: Fix Test Data UUIDs ⏳
File:
openespi-common/src/test/java/org/greenbuttonalliance/espi/common/service/impl/DtoExportServiceImplTest.javaIssue: Test entity UUIDs use Version-4 (random) instead of Version-5 (deterministic)
Example (line 118):
Fix:
ESPI Requirement: Version-5 UUIDs are deterministic (based on namespace + name using SHA-1 hash). Format:
xxxxxxxx-xxxx-5xxx-xxxx-xxxxxxxxxxxxFiles to Update:
Step 5: Add Comprehensive Assertions to DtoExportServiceImplTest ⏳
File:
openespi-common/src/test/java/org/greenbuttonalliance/espi/common/service/impl/DtoExportServiceImplTest.javaCurrent Issue: Test only prints XML output with
System.out.println()- no validationRequired Assertions:
@Disableduntil DtoExportServiceImpl fixed - see issue Convert XML marshalling tests from JAXB to Jackson 3 XmlMapper #62)@Disableduntil DtoExportServiceImpl fixed - see issue Convert XML marshalling tests from JAXB to Jackson 3 XmlMapper #62)See:
JACKSON3_XML_MARSHALLING_TEST_PLAN.mdfor detailed assertion examplesStep 6: Create GitHub Issue for DtoExportServiceImpl Enhancement ⏳
DtoExportServiceImpl Issues to Document:
Version-4 UUIDs instead of Version-5:
createAtomEntry()usesUUID.randomUUID()(Version-4 random)entity.getId()(Version-5 from database)Missing Atom metadata extraction:
<title>is empty - should useentity.getDescription()<link>elements are NULL - should extract fromentity.selfLink,upLink,relatedLinksCorrect behavior:
<published>and<updated>use NOW (correct)Reference:
JACKSON3_XML_MARSHALLING_TEST_PLAN.mdIssue #1 (lines 21-67)Step 7: Update Documentation and Create PR ⏳
Files to Update:
JACKSON3_XML_MARSHALLING_TEST_PLAN.md(already exists)PR Creation:
feature/jackson3-xml-marshalling-testsTechnical Details
Jackson 3 XmlMapper Configuration
All tests use this standard configuration:
Key Differences: Jackson 3 vs JAXB
Namespace Handling:
<espi:tzOffset>)<tzOffset>withxmlns="..."on root)Marshalling API:
marshaller.marshal(object, writer)xmlMapper.writeValueAsString(object)Unmarshalling API:
(Type) unmarshaller.unmarshal(reader)xmlMapper.readValue(xml, Type.class)Assertions:
assertTrue,assertEquals,assertArrayEquals)assertThat().contains(),isEqualTo(),isNotNull())POJO DTO Issues Found
Problem: TimeConfigurationDto and UsagePointDto are POJOs (not records) with
@XmlAccessorType(XmlAccessType.PROPERTY)Impact: ALL public getters are serialized, including utility methods
Temporary Fix: Add
@XmlTransientto utility method gettersLong-term Solution: Convert to records with
@XmlAccessorType(XmlAccessType.FIELD)(see issue #61)Why Records Are Better:
@XmlTransientTemplate: See
ReadingTypeDto.javafor record patternTest Results Summary
Total Tests Converted: 18 tests
Files Modified:
TimeConfigurationDtoTest.java- Converted to Jackson 3TimeConfigurationDto.java- Added@XmlTransientto utility methodsJackson3XmlMarshallingTest.java- Created (renamed from SimpleXmlMarshallingTest)UsagePointDto.java- Added@XmlTransientto utility methodsSimpleXmlMarshallingTest.java- Deleted (replaced by Jackson3XmlMarshallingTest)Remaining Files:
XmlDebugTest.java- Needs conversionDtoExportServiceImplTest.java- Needs UUID fixes and assertionsDependencies
Related Issues:
Related Files:
JACKSON3_XML_MARSHALLING_TEST_PLAN.md- Comprehensive test conversion planDTO_APPROACH_COMPARISON.md- Jackson 3 + JAXB decision documentationReadingTypeDto.java- Record pattern templateSuccess Criteria
Branch:
feature/jackson3-xml-marshalling-testsAssignee: @donal (or appropriate team member)
Labels: testing, jackson, xml-marshalling, espi-compliance
Milestone: Spring Boot 4.0 + Java 25 Migration
Author: Claude Sonnet 4.5
Date: 2026-01-05
Progress: Steps 1-2 complete (18/18 tests passing), Steps 3-7 remaining