Skip to content

Conversation

@bene-tyler
Copy link

Add opt-in environment flag to automatically wrap non-array values in arrays when used in for loops. This solves the common XML-to-JSON cardinality problem where single items are objects but multiple items are arrays.

Problem solved:

  • XML with one converts to object: {"person": {...}}
  • XML with multiple converts to array: {"person": [{...}, {...}]}
  • Previously required different templates or custom ensureArray() callback or is_array() if statement
  • Now no custom code is needed to use a for loop with json objects that change cardinality per request.

Usage:
env.set_ensure_array_for_loops(true); // {% for p in person %} now works whether person is object or array

Implementation:

  • Added RenderConfig::ensure_array_for_loops flag (defaults to false)
  • Added Environment::set_ensure_array_for_loops() method
  • Created ensure_array() helper function that wraps non-arrays:
    • Arrays: returned unchanged
    • Null: becomes empty array []
    • Objects/primitives: wrapped in single-item array [value]
  • Modified ForArrayStatementNode::visit() to call ensure_array() when enabled

Testing:

  • 26 new test assertions in test-ensure-array.cpp
  • Tests single objects, arrays, null, primitives, nested loops
  • Tests default behavior, enabled behavior, and disabled flag
  • All 284 assertions pass (26 new + 258 existing)
  • No regressions in existing functionality
  • Stress tested with 10,000 iterations

Backward compatibility:

  • 100% backward compatible - flag defaults to false
  • Without explicit enablement, behavior is identical to previous versions
  • Non-arrays still throw errors by default
  • Existing code requires zero changes

Documentation:

  • Added usage example to README.md
  • Comprehensive test suite demonstrates all use cases

Add opt-in environment flag to automatically wrap non-array values in arrays
when used in for loops. This solves the common XML-to-JSON cardinality problem
where single items are objects but multiple items are arrays.

Problem solved:
- XML with one <person> converts to object: {"person": {...}}
- XML with multiple <person> converts to array: {"person": [{...}, {...}]}
- Previously required different templates or custom ensureArray() callback
- Now no custom code is needed to use a for loop with json objects that change cardinality per request.

Usage:
  env.set_ensure_array_for_loops(true);
  // {% for p in person %} now works whether person is object or array

Implementation:
- Added RenderConfig::ensure_array_for_loops flag (defaults to false)
- Added Environment::set_ensure_array_for_loops() method
- Created ensure_array() helper function that wraps non-arrays:
  * Arrays: returned unchanged
  * Null: becomes empty array []
  * Objects/primitives: wrapped in single-item array [value]
- Modified ForArrayStatementNode::visit() to call ensure_array() when enabled

Testing:
- 26 new test assertions in test-ensure-array.cpp
- Tests single objects, arrays, null, primitives, nested loops
- Tests default behavior, enabled behavior, and disabled flag
- All 284 assertions pass (26 new + 258 existing)
- No regressions in existing functionality
- Stress tested with 10,000 iterations

Backward compatibility:
- 100% backward compatible - flag defaults to false
- Without explicit enablement, behavior is identical to previous versions
- Non-arrays still throw errors by default
- Existing code requires zero changes

Documentation:
- Added usage example to README.md
- Comprehensive test suite demonstrates all use cases
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant