如何优化Apache Velocity的JSON转PACT模板?避免引入Java类
Absolutely, there’s a straightforward way to rewrite your Velocity template to ditch reliance on Java classes like JSONStringer—this lets your testing team craft and modify templates directly, no Java code required. Here’s how to pull it off:
1. Leverage Velocity’s Built-In Directives for JSON Structure
JSON is just structured text, so we can build it directly using Velocity’s core features: variable references, loops, and conditionals. No external helpers needed.
Key Concepts to Use:
- Variable References: Access input JSON fields directly with
$input.yourFieldName(assuming your input is passed into the Velocity context as$input). #foreachLoops: Iterate over arrays or object entries to build dynamic JSON arrays/objects.#ifConditionals: Handle optional fields to avoid invalid JSON (like null values or empty objects).- Comma Handling: Use
$foreach.hasNextto add commas only between elements, preventing trailing commas that break JSON validity.
2. Example Template Rewrite
Let’s say your original template used JSONStringer to serialize parts of the input. Here’s how to replace that with pure Velocity:
Sample Input JSON (Simplified):
{ "interactions": [ { "description": "Get user details", "request": { "method": "GET", "path": "/users/123", "headers": {"Content-Type": "application/json"} }, "response": { "status": 200, "body": {"id": 123, "name": "Alice"} } } ] }
Pure Velocity Template (No Java Helpers):
{ "consumer": { "name": "$input.consumer.name" }, "provider": { "name": "$input.provider.name" }, "interactions": [ #foreach($interaction in $input.interactions) { "description": "$interaction.description", "request": { "method": "$interaction.request.method", "path": "$interaction.request.path", #if($interaction.request.headers) "headers": { #foreach($headerEntry in $interaction.request.headers.entrySet()) "$headerEntry.key": "$headerEntry.value"#if($foreach.hasNext),#end #end }, #end #if($interaction.request.query) "query": { #foreach($queryEntry in $interaction.request.query.entrySet()) "$queryEntry.key": "$queryEntry.value"#if($foreach.hasNext),#end #end } #end }, "response": { "status": $interaction.response.status, #if($interaction.response.body) "body": { #foreach($bodyEntry in $interaction.response.body.entrySet()) "$bodyEntry.key": $bodyEntry.value#* Handle numbers/booleans without quotes *# #if($foreach.hasNext),#end #end } #end } }#if($foreach.hasNext),#end #end ], "metadata": { "pactSpecification": { "version": "2.0.0" } } }
3. Handling Edge Cases
Special Character Escaping
If your input fields contain double quotes or backslashes, use Velocity’s built-in string replacement to escape them without Java classes:
"description": "$interaction.description.replace('"', '\\"').replace('\\', '\\\\')"
Nested Arrays/Objects
For deeply nested structures, nest #foreach loops or split reusable logic into sub-templates (use #parse("header-template.vm") to include them) to keep your main template clean.
Non-String Values (Numbers/Booleans)
Avoid wrapping numbers or booleans in quotes by checking the type (or if you know the input type, just omit the quotes):
"status": $interaction.response.status #* Outputs as a number, not a string *# "isActive": $interaction.response.body.isActive #* Outputs true/false without quotes *#
4. Why This Works
This approach leans entirely on Velocity’s native capabilities—no external Java libraries or custom helpers required. Testers only need to know basic Velocity syntax and PACT JSON structure to modify the template, making it accessible and maintainable for your team.
内容的提问来源于stack exchange,提问作者hello1693

