Annotation mode keeps requests close to Java, but large integrations raise a fair question: do I want a wall of curl strings inside my service interfaces? JQuickCurl's XML mode answers with a firm no — you store every API definition in a separate XML file, and the business layer only sees plain Java interfaces with no annotations at all. This is the classic "declarative client" split: contract in configuration, behavior in code.
In this post you'll learn to:
- Author an XML API catalog with the official DTD.
- Bind it to a plain Java interface automatically.
- Use
#{...}context variables in XML. - Swap endpoint behavior without recompiling business code.
The Big Idea
apis.xml (curl commands + return types) business code
| |
+-- parsed by JQuickCurlXmlParseFactory ----------> plain interface proxy
The XML file names each request and declares its return type. The factory binds those entries to interface methods by method name. Change the curl in XML → the running app changes behavior. No Java edits, no rebuild.
Step 1: Write the XML Catalog
Save this as src/main/resources/apis.xml. The DOCTYPE references the bundled DTD so your IDE can validate structure as you type.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE curls PUBLIC "-//PAOHAIJIAO//DTD API CURL 1.0//EN"
"classpath:paohaijiao/dtd/Jquick-curl.dtd">
<curls namespace="com.example.weather.WeatherApi">
<curl name="current" returnClass="java.lang.String">
curl -X GET 'https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41¤t_weather=true'
</curl>
<curl name="currentAt" returnClass="java.lang.String">
curl -X GET 'https://api.open-meteo.com/v1/forecast?latitude=#{lat}&longitude=#{lon}¤t_weather=true'
</curl>
<curl name="sunrise" returnClass="java.lang.String">
curl -X GET 'https://api.sunrise-sunset.org/json?lat=#{lat}&lng=#{lon}&formatted=0'
</curl>
</curls>
Rules to remember while authoring:
-
<curls namespace="...">identifies the interface the file maps to. - Each
<curl>hasname(must equal the Java method name) andreturnClass(fully qualified). - The text content is the curl command;
#{name}placeholders are resolved at runtime from context/method parameters. - Optional
<if>,<foreach>, and<choose>children make content conditional (Post 8 covers<if>in depth).
Step 2: Write a Pure Java Interface
No annotations, no HTTP imports — just signatures matching the name attributes. Parameters are bound with @Param("...") (import from the xml module) so the XML can reference them via #{...}:
import com.github.paohaijiao.xml.param.Param;
public interface WeatherApi {
String current();
String currentAt(@Param("lat") double lat, @Param("lon") double lon);
String sunrise(@Param("lat") double lat, @Param("lon") double lon);
}
Step 3: Build the Proxy from the XML Factory
JQuickCurl ships its own parse handler; the generic XML-proxy factory comes from the companion library:
import com.github.paohaijiao.xml.JQuickCurlXmlParseFactory;
import com.github.paohaijiao.xml.factory.JQuickXmlFactory;
import com.github.paohaijiao.xml.handler.JQuickParseHandler;
public class XmlDemo {
public static void main(String[] args) {
// 1. Parser that understands JQuickCurl XML.
JQuickParseHandler parser = new JQuickCurlXmlParseFactory();
// 2. Factory bound to the classpath resource "apis.xml".
JQuickXmlFactory factory = new JQuickXmlFactory(parser, "apis.xml");
// 3. One proxy for the whole catalog.
WeatherApi api = factory.createApi(WeatherApi.class);
System.out.println(api.current());
System.out.println(api.currentAt(52.52, 13.41));
System.out.println(api.sunrise(52.52, 13.41));
}
}
Expected output: JSON from Open-Meteo and the sunrise/sunset API. The calls above run without authentication and prove the whole round trip.
A Second Catalog Without Touching Java
Want to point current at a different provider, add a header, or insert retry semantics? Edit only the XML:
<curl name="current" returnClass="java.lang.String">
curl -X GET 'https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41¤t_weather=true' \
-H 'Accept: application/json'
</curl>
The proxy, the interface, and all callers stay unchanged. That is the entire value proposition: API drift is fixed in one configuration file, not across dozens of call sites.
XML Mode vs Annotation Mode — When to Choose Which
| Dimension | Annotation mode (@JCurlCommand) |
XML mode (apis.xml) |
|---|---|---|
| Request location | Next to Java code | Centralized config file |
| Change without rebuild | No | Yes |
| IDE help | Java syntax | XML + DTD validation |
| Variables |
${name} from JQuickCurlReq
|
#{name} from context / @Param args |
| Best fit | Few endpoints, team owns the code | Large third-party API catalogs, shared config |
Summary
XML configuration mode decouples what the HTTP call is from who calls it. You get one searchable, validated, DTD-checked catalog of every integration your service owns, while Java interfaces stay free of annotation noise. This pattern pays off exactly when your API surface is wide and changes often.
Source project: dromara/jquick-curl. Post 8 pushes XML mode further with <if> conditions that render parts of a request only when runtime state says so.
Top comments (0)