
HAL client handling HAL document parsing, RFC6570 URI-template expansion and HTTP communication with HAL-aware helpers, easy link/embedded navigation, multipart/file uploads, and structured error handling.
= HALDiSh — Kotlin Multiplatform HAL client :toc: left :toclevels: 3 :icons: font :source-highlighter: highlight.js
image:https://img.shields.io/maven-central/v/com.helpchoice.nahal/haldish.svg?label=Maven%20Central[Maven Central,link=https://central.sonatype.com/artifact/com.helpchoice.nahal/haldish] image:https://img.shields.io/badge/License-Apache%202.0-blue.svg[License,link=https://www.apache.org/licenses/LICENSE-2.0]
Haldish is a https://stateless.group/hal_specification.html[HAL (Hypertext Application Language)] client library for https://kotlinlang.org/docs/multiplatform.html[Kotlin Multiplatform]. It handles HAL document parsing, RFC 6570 URI template expansion, and HTTP communication on every supported platform.
This repository holds the library alone. The NaHAL navigator built on it — :core, the Compose
UI, the example plugins and the testkit — lives in ../nahal and consumes this project as the
published artifact com.helpchoice.nahal:haldish.
== Supported Platforms
|=== | Target | HTTP engine
| JVM | Ktor CIO
| JS (IR) — browser + Node.js | ktor-client-js
| Wasm/JS — browser | ktor-client-js
| macOS x64 / arm64 | Ktor Darwin
| iOS x64 / arm64 / Simulator arm64 | Ktor Darwin
| Linux x64 / arm64 | Ktor cURL
| Windows (mingwX64) | Ktor WinHTTP |===
YAML HAL format (application/hal+yaml) is supported on JVM and JS only. On native and Wasm targets, YAML responses are not parsed.
== Dependency
Released on Maven Central as com.helpchoice.nahal:haldish. Current version: 2.0.0.
In a Kotlin Multiplatform project, declare it once in commonMain — Gradle module metadata
resolves the right per-target artifact (haldish-jvm, haldish-js, haldish-macosarm64, …)
for every target you build:
// build.gradle.kts repositories { mavenCentral() }
For a plain JVM (or Android) project, the same coordinates work — Gradle picks haldish-jvm:
Maven has no support for Gradle module metadata, so name the JVM artifact explicitly:
com.helpchoice.nahal haldish-jvm 2.0.0 ----Snapshots are not published. To try unreleased changes, build from source and install into the local Maven repository:
== Quick Start (Kotlin)
import com.helpchoice.nahal.haldish.http.HalHttpClient import com.helpchoice.nahal.haldish.uritemplate.expandHref
// HalHttpClient wraps a Ktor HttpClient and adds HAL-aware helpers.
// It implements AutoCloseable — use use or call close() when done.
val client = HalHttpClient()
// GET a HAL document (sends Accept: application/hal+json, hal+xml, hal+yaml, …) val root: HalDocument = client.getHal("https://api.example.com/")
// Navigate links val self: HalLink? = root.link("self") // first link for rel val admins: List = root.links("admin") // all links for rel
// Read embedded resources val orders: List = root.embedded("ea:order")
// Read plain properties val count: JsonElement? = root.properties["currentlyProcessing"]
=== URI Template Expansion
HAL links can be https://www.rfc-editor.org/rfc/rfc6570[RFC 6570 URI templates].
Use expandHref or UriTemplate to expand them before making a request.
import com.helpchoice.nahal.haldish.uritemplate.UriTemplate import com.helpchoice.nahal.haldish.uritemplate.UriTemplateVars import com.helpchoice.nahal.haldish.uritemplate.expandHref
// Scalar variable val link = root.link("search")!! // href: "/items{?q,page}" val url = link.expandHref("q" to "hal", "page" to 2) // → /items?q=hal&page=2
// List variable val vars = UriTemplateVars() .add("name", "Project A") .add("name", "Project B") .set("name_op", "in") val filtered = UriTemplate("/items{?name*,name_op}").expand(vars) // → /items?name=Project%20A&name=Project%20B&name_op=in
UriTemplateVars supports three value types:
set(name, scalar) — single string valueset(name, list) / add(name, value) — ordered listset(name, map) / put(name, field, value) — associative map=== Making Requests
import com.helpchoice.nahal.haldish.http.HalHttpClient import com.helpchoice.nahal.haldish.http.HalRequestBody
val client = HalHttpClient()
// Raw HTTP — returns HalHttpResponse (statusCode, headers, cookies, body, contentType) val response = client.get(url) val created = client.post(url, HalRequestBody.Json("""{"name":"foo"}""")) val updated = client.patch(url, HalRequestBody.Json("""{"name":"bar"}""")) val deleted = client.delete(url)
// File upload val bytes: ByteArray = java.io.File("photo.jpg").readBytes() client.post(url, HalRequestBody.Binary(bytes, "image/jpeg"))
// Multipart client.post(url, HalRequestBody.Multipart(listOf( MultipartPart("photo", photoBytes, fileName = "photo.jpg", contentType = "image/jpeg"), MultipartPart("metadata", metaBytes, contentType = "application/json"), )))
// Custom headers and cookies client.get(url, headers = mapOf("Authorization" to "Bearer token"), cookies = mapOf("session" to "abc123"), )
=== Error Handling
All exceptions extend HaldishException:
== Java
HalHttpClient is usable from Java. Kotlin suspend functions are called via BuildersKt.runBlocking:
try (HalHttpClient client = new HalHttpClient()) { HalDocument root = (HalDocument) BuildersKt.runBlocking( EmptyCoroutineContext.INSTANCE, (scope, cont) -> client.getHal("https://api.example.com/", Map.of(), cont) ); String href = root.link("projects").getHref();
UriTemplateVars vars = new UriTemplateVars()
.set("name", Arrays.asList("Project A", "Project B"))
.set("name_op", "in");
String url = new UriTemplate(href).expand(vars);
A full walkthrough is in link:examples/java/CrudExample.java[].
== JavaScript (Node.js)
=== From Maven Central (Kotlin/JS projects)
A Kotlin/JS project takes the dependency and needs no local build of this repository:
=== From npm (plain JavaScript)
The package ships a facade that flattens the compiled output's Kotlin package namespaces, so both
entry styles resolve, and hand-written index.d.ts declarations give TypeScript consumers types.
Central's haldish-js-2.0.1.klib is Kotlin-compiler input only — the npm package is what plain
JavaScript consumes.
Publishing is wired through dev.petuska.npm.publish:
=== From source (plain JavaScript)
To build the library directly rather than installing the package:
./gradlew jsNodeProductionLibraryDistribution
CAUTION: The JS target currently emits UMD, not ES modules, so exports arrive nested under
their Kotlin package rather than as flat named exports. useEsModules() cannot be enabled yet —
see "Known limitation" below.
const { JsHalClient, JsHeaders, JsMultipartPart } = require('./build/compileSync/js/main/productionLibrary/kotlin/haldish.js') .com.helpchoice.nahal.haldish;
const client = new JsHalClient();
// All HTTP methods return Promise ({ statusCode, body }) const resp = await client.get('https://api.example.com/'); const root = JSON.parse(resp.body); const tmpl = root._links.projects.href;
// URI template expansion (builder pattern) client.varsAdd('name', 'Project A'); client.varsAdd('name', 'Project B'); client.varsSet('name_op', 'in'); const url = client.expandVars(tmpl); // expands and resets the builder
// Custom headers const headers = new JsHeaders() .set('Authorization', 'Bearer token') .set('Accept', 'application/hal+xml'); const r = await client.get(url, headers);
// File upload (bytes must be Int8Array) await client.postFile(url, int8Array, 'image/jpeg');
// Multipart const parts = [ new JsMultipartPart('photo', int8Array, 'image/jpeg', 'photo.jpg'), new JsMultipartPart('metadata', metaBytes, 'application/json'), ]; await client.postMultipart(url, parts);
Full example: link:examples/javascript/crud-example.mjs[] — note it is written against ES module output and does not run until the limitation below is resolved.
=== Known limitation: no ES module output
Enabling useEsModules() on the js(IR) target would give flat named exports, .mjs files and
a cleaner .d.ts — but it currently breaks every JS test task. Under ES modules the compiler
emits xmlutil-core's eleven identically named top-level function appendChild(node) declarations
into a single module scope. That is legal inside the UMD wrapper's function scope and a
SyntaxError in an ES module, so both jsNodeTest and jsBrowserTest fail to parse the test
bundle. Release output only escapes it because dead-code elimination drops those functions.
Reproduced on Kotlin 2.1.20 and 2.2.21. Per-file output granularity swaps the failure for a stdlib metadata-initialisation crash; xmlutil 1.0.2 does not compile against the current parser sources. Until one of those paths opens, the JS target stays on UMD.
== WebAssembly (Browser)
=== From Maven Central (Kotlin/Wasm projects)
kotlin { @OptIn(ExperimentalWasmDsl::class) wasmJs { browser() }
sourceSets {
wasmJsMain.dependencies {
implementation("com.helpchoice.nahal:haldish:2.0.1")
}
}
Note that YamlHalParser is a stub on Wasm — kaml is unavailable there, so application/hal+yaml
responses are not parsed.
=== From npm (plain JavaScript)
Shipped as a package of its own rather than an entry point of haldish: the two APIs differ
(classes vs. module-level functions), so no exports condition could swap them transparently, and
bundling them would push the browser-only 622 KB .wasm onto every Node consumer.
=== From source (plain JavaScript)
To build the .mjs and its .wasm payload directly:
./gradlew wasmJsBrowserProductionLibraryDistribution
Unlike the JS target, Kotlin/Wasm always emits ES modules, so these import normally.
The Wasm module must be served over HTTP (not file://) because browsers block Wasm loading from local files:
npx serve . # from the repo root
Kotlin/Wasm restricts @JsExport to functions only (classes are not supported), so the Wasm API is exposed as module-level functions with shared state — the same pattern as the native C API.
After awaiting any HTTP call, read the result with wasmLastStatus() and wasmLastBody().
The generated .mjs uses top-level await to initialise the Wasm runtime; all exports are ready as soon as the import resolves.
import { wasmGet, wasmPost, wasmPatch, wasmDelete, wasmPostFile, wasmPartAdd, wasmPartsClear, wasmPostMultipart, wasmLastStatus, wasmLastBody, wasmHeaderSet, wasmHeadersClear, wasmVarsSet, wasmVarsAdd, wasmExpandVars, wasmClose, } from './build/compileSync/wasmJs/main/productionLibrary/kotlin/haldish-wasm-js.mjs';
// GET — read result after awaiting await wasmGet('https://api.example.com/'); const root = JSON.parse(wasmLastBody()); const tmpl = root._links.projects.href;
// URI template expansion (builder pattern, same as JS) wasmVarsAdd('name', 'Project A'); wasmVarsAdd('name', 'Project B'); wasmVarsSet('name_op', 'in'); const url = wasmExpandVars(tmpl); // expands and resets the builder
// Custom headers (consumed and cleared after the next HTTP call) wasmHeaderSet('Authorization', 'Bearer token'); wasmHeaderSet('Accept', 'application/hal+xml'); await wasmGet(url);
// File upload (bytes as Int8Array) await wasmPostFile(url, int8Array, 'image/jpeg');
// Multipart wasmPartAdd('photo', int8Array, 'image/jpeg', 'photo.jpg'); wasmPartAdd('metadata', metaBytes, 'application/json', null); await wasmPostMultipart(url);
Full example: link:examples/wasm/crud-example.html[].
== Native (C / C++)
Build the shared library for your target:
./gradlew linkReleaseSharedMacosArm64
./gradlew linkReleaseSharedLinuxX64
Output (e.g., macOS arm64):
Compile and link (macOS example):
The C API (NativeCApi.kt) exposes functions via @CName:
// HTTP const char* haldish_get(const char* url); const char* haldish_post_json(const char* url, const char* json); const char* haldish_patch_json(const char* url, const char* json); int haldish_delete(const char* url); const char* haldish_post_file(const char* url, int8_t* data, int size, const char* contentType);
// Last-response metadata (read after any HTTP call) int haldish_last_status(); const char* haldish_last_content_type();
// Request headers (consumed on the next HTTP call) void haldish_headers_set(const char* key, const char* value); void haldish_headers_clear();
// Multipart builder void haldish_part_add(const char* name, int8_t* data, int size, const char* contentType, const char* fileName); void haldish_parts_clear(); const char* haldish_post_multipart(const char* url);
// HAL navigation (parse body, extract links) const char* haldish_link_href(const char* body, const char* contentType, const char* rel); const char* haldish_first_embedded_self(const char* body, const char* contentType, const char* embeddedRel);
// URI template expansion const char* haldish_expand(const char* tmpl); // no variables void haldish_vars_set(const char* key, const char* value); void haldish_vars_add(const char* key, const char* value); void haldish_vars_reset(); const char* haldish_expand_vars(const char* tmpl); // expand + reset builder
Full example: link:examples/cpp/crud_example.cpp[].
== HAL Format Detection
When no Content-Type header is present, the parser sniffs the body:
|=== | Starts with | Detected format
| { or [
| JSON
| <
| XML
| anything else (non-empty) | YAML |===
Explicit content types take precedence: application/hal+json, application/json, application/hal+xml, application/xml, text/xml, application/hal+yaml, application/yaml, text/yaml.
== Building
./gradlew build
./gradlew jvmTest
== Related Tools
== License
link:https://www.apache.org/licenses/LICENSE-2.0[Apache License 2.0]
= HALDiSh — Kotlin Multiplatform HAL client :toc: left :toclevels: 3 :icons: font :source-highlighter: highlight.js
image:https://img.shields.io/maven-central/v/com.helpchoice.nahal/haldish.svg?label=Maven%20Central[Maven Central,link=https://central.sonatype.com/artifact/com.helpchoice.nahal/haldish] image:https://img.shields.io/badge/License-Apache%202.0-blue.svg[License,link=https://www.apache.org/licenses/LICENSE-2.0]
Haldish is a https://stateless.group/hal_specification.html[HAL (Hypertext Application Language)] client library for https://kotlinlang.org/docs/multiplatform.html[Kotlin Multiplatform]. It handles HAL document parsing, RFC 6570 URI template expansion, and HTTP communication on every supported platform.
This repository holds the library alone. The NaHAL navigator built on it — :core, the Compose
UI, the example plugins and the testkit — lives in ../nahal and consumes this project as the
published artifact com.helpchoice.nahal:haldish.
== Supported Platforms
|=== | Target | HTTP engine
| JVM | Ktor CIO
| JS (IR) — browser + Node.js | ktor-client-js
| Wasm/JS — browser | ktor-client-js
| macOS x64 / arm64 | Ktor Darwin
| iOS x64 / arm64 / Simulator arm64 | Ktor Darwin
| Linux x64 / arm64 | Ktor cURL
| Windows (mingwX64) | Ktor WinHTTP |===
YAML HAL format (application/hal+yaml) is supported on JVM and JS only. On native and Wasm targets, YAML responses are not parsed.
== Dependency
Released on Maven Central as com.helpchoice.nahal:haldish. Current version: 2.0.0.
In a Kotlin Multiplatform project, declare it once in commonMain — Gradle module metadata
resolves the right per-target artifact (haldish-jvm, haldish-js, haldish-macosarm64, …)
for every target you build:
// build.gradle.kts repositories { mavenCentral() }
For a plain JVM (or Android) project, the same coordinates work — Gradle picks haldish-jvm:
Maven has no support for Gradle module metadata, so name the JVM artifact explicitly:
com.helpchoice.nahal haldish-jvm 2.0.0 ----Snapshots are not published. To try unreleased changes, build from source and install into the local Maven repository:
== Quick Start (Kotlin)
import com.helpchoice.nahal.haldish.http.HalHttpClient import com.helpchoice.nahal.haldish.uritemplate.expandHref
// HalHttpClient wraps a Ktor HttpClient and adds HAL-aware helpers.
// It implements AutoCloseable — use use or call close() when done.
val client = HalHttpClient()
// GET a HAL document (sends Accept: application/hal+json, hal+xml, hal+yaml, …) val root: HalDocument = client.getHal("https://api.example.com/")
// Navigate links val self: HalLink? = root.link("self") // first link for rel val admins: List = root.links("admin") // all links for rel
// Read embedded resources val orders: List = root.embedded("ea:order")
// Read plain properties val count: JsonElement? = root.properties["currentlyProcessing"]
=== URI Template Expansion
HAL links can be https://www.rfc-editor.org/rfc/rfc6570[RFC 6570 URI templates].
Use expandHref or UriTemplate to expand them before making a request.
import com.helpchoice.nahal.haldish.uritemplate.UriTemplate import com.helpchoice.nahal.haldish.uritemplate.UriTemplateVars import com.helpchoice.nahal.haldish.uritemplate.expandHref
// Scalar variable val link = root.link("search")!! // href: "/items{?q,page}" val url = link.expandHref("q" to "hal", "page" to 2) // → /items?q=hal&page=2
// List variable val vars = UriTemplateVars() .add("name", "Project A") .add("name", "Project B") .set("name_op", "in") val filtered = UriTemplate("/items{?name*,name_op}").expand(vars) // → /items?name=Project%20A&name=Project%20B&name_op=in
UriTemplateVars supports three value types:
set(name, scalar) — single string valueset(name, list) / add(name, value) — ordered listset(name, map) / put(name, field, value) — associative map=== Making Requests
import com.helpchoice.nahal.haldish.http.HalHttpClient import com.helpchoice.nahal.haldish.http.HalRequestBody
val client = HalHttpClient()
// Raw HTTP — returns HalHttpResponse (statusCode, headers, cookies, body, contentType) val response = client.get(url) val created = client.post(url, HalRequestBody.Json("""{"name":"foo"}""")) val updated = client.patch(url, HalRequestBody.Json("""{"name":"bar"}""")) val deleted = client.delete(url)
// File upload val bytes: ByteArray = java.io.File("photo.jpg").readBytes() client.post(url, HalRequestBody.Binary(bytes, "image/jpeg"))
// Multipart client.post(url, HalRequestBody.Multipart(listOf( MultipartPart("photo", photoBytes, fileName = "photo.jpg", contentType = "image/jpeg"), MultipartPart("metadata", metaBytes, contentType = "application/json"), )))
// Custom headers and cookies client.get(url, headers = mapOf("Authorization" to "Bearer token"), cookies = mapOf("session" to "abc123"), )
=== Error Handling
All exceptions extend HaldishException:
== Java
HalHttpClient is usable from Java. Kotlin suspend functions are called via BuildersKt.runBlocking:
try (HalHttpClient client = new HalHttpClient()) { HalDocument root = (HalDocument) BuildersKt.runBlocking( EmptyCoroutineContext.INSTANCE, (scope, cont) -> client.getHal("https://api.example.com/", Map.of(), cont) ); String href = root.link("projects").getHref();
UriTemplateVars vars = new UriTemplateVars()
.set("name", Arrays.asList("Project A", "Project B"))
.set("name_op", "in");
String url = new UriTemplate(href).expand(vars);
A full walkthrough is in link:examples/java/CrudExample.java[].
== JavaScript (Node.js)
=== From Maven Central (Kotlin/JS projects)
A Kotlin/JS project takes the dependency and needs no local build of this repository:
=== From npm (plain JavaScript)
The package ships a facade that flattens the compiled output's Kotlin package namespaces, so both
entry styles resolve, and hand-written index.d.ts declarations give TypeScript consumers types.
Central's haldish-js-2.0.1.klib is Kotlin-compiler input only — the npm package is what plain
JavaScript consumes.
Publishing is wired through dev.petuska.npm.publish:
=== From source (plain JavaScript)
To build the library directly rather than installing the package:
./gradlew jsNodeProductionLibraryDistribution
CAUTION: The JS target currently emits UMD, not ES modules, so exports arrive nested under
their Kotlin package rather than as flat named exports. useEsModules() cannot be enabled yet —
see "Known limitation" below.
const { JsHalClient, JsHeaders, JsMultipartPart } = require('./build/compileSync/js/main/productionLibrary/kotlin/haldish.js') .com.helpchoice.nahal.haldish;
const client = new JsHalClient();
// All HTTP methods return Promise ({ statusCode, body }) const resp = await client.get('https://api.example.com/'); const root = JSON.parse(resp.body); const tmpl = root._links.projects.href;
// URI template expansion (builder pattern) client.varsAdd('name', 'Project A'); client.varsAdd('name', 'Project B'); client.varsSet('name_op', 'in'); const url = client.expandVars(tmpl); // expands and resets the builder
// Custom headers const headers = new JsHeaders() .set('Authorization', 'Bearer token') .set('Accept', 'application/hal+xml'); const r = await client.get(url, headers);
// File upload (bytes must be Int8Array) await client.postFile(url, int8Array, 'image/jpeg');
// Multipart const parts = [ new JsMultipartPart('photo', int8Array, 'image/jpeg', 'photo.jpg'), new JsMultipartPart('metadata', metaBytes, 'application/json'), ]; await client.postMultipart(url, parts);
Full example: link:examples/javascript/crud-example.mjs[] — note it is written against ES module output and does not run until the limitation below is resolved.
=== Known limitation: no ES module output
Enabling useEsModules() on the js(IR) target would give flat named exports, .mjs files and
a cleaner .d.ts — but it currently breaks every JS test task. Under ES modules the compiler
emits xmlutil-core's eleven identically named top-level function appendChild(node) declarations
into a single module scope. That is legal inside the UMD wrapper's function scope and a
SyntaxError in an ES module, so both jsNodeTest and jsBrowserTest fail to parse the test
bundle. Release output only escapes it because dead-code elimination drops those functions.
Reproduced on Kotlin 2.1.20 and 2.2.21. Per-file output granularity swaps the failure for a stdlib metadata-initialisation crash; xmlutil 1.0.2 does not compile against the current parser sources. Until one of those paths opens, the JS target stays on UMD.
== WebAssembly (Browser)
=== From Maven Central (Kotlin/Wasm projects)
kotlin { @OptIn(ExperimentalWasmDsl::class) wasmJs { browser() }
sourceSets {
wasmJsMain.dependencies {
implementation("com.helpchoice.nahal:haldish:2.0.1")
}
}
Note that YamlHalParser is a stub on Wasm — kaml is unavailable there, so application/hal+yaml
responses are not parsed.
=== From npm (plain JavaScript)
Shipped as a package of its own rather than an entry point of haldish: the two APIs differ
(classes vs. module-level functions), so no exports condition could swap them transparently, and
bundling them would push the browser-only 622 KB .wasm onto every Node consumer.
=== From source (plain JavaScript)
To build the .mjs and its .wasm payload directly:
./gradlew wasmJsBrowserProductionLibraryDistribution
Unlike the JS target, Kotlin/Wasm always emits ES modules, so these import normally.
The Wasm module must be served over HTTP (not file://) because browsers block Wasm loading from local files:
npx serve . # from the repo root
Kotlin/Wasm restricts @JsExport to functions only (classes are not supported), so the Wasm API is exposed as module-level functions with shared state — the same pattern as the native C API.
After awaiting any HTTP call, read the result with wasmLastStatus() and wasmLastBody().
The generated .mjs uses top-level await to initialise the Wasm runtime; all exports are ready as soon as the import resolves.
import { wasmGet, wasmPost, wasmPatch, wasmDelete, wasmPostFile, wasmPartAdd, wasmPartsClear, wasmPostMultipart, wasmLastStatus, wasmLastBody, wasmHeaderSet, wasmHeadersClear, wasmVarsSet, wasmVarsAdd, wasmExpandVars, wasmClose, } from './build/compileSync/wasmJs/main/productionLibrary/kotlin/haldish-wasm-js.mjs';
// GET — read result after awaiting await wasmGet('https://api.example.com/'); const root = JSON.parse(wasmLastBody()); const tmpl = root._links.projects.href;
// URI template expansion (builder pattern, same as JS) wasmVarsAdd('name', 'Project A'); wasmVarsAdd('name', 'Project B'); wasmVarsSet('name_op', 'in'); const url = wasmExpandVars(tmpl); // expands and resets the builder
// Custom headers (consumed and cleared after the next HTTP call) wasmHeaderSet('Authorization', 'Bearer token'); wasmHeaderSet('Accept', 'application/hal+xml'); await wasmGet(url);
// File upload (bytes as Int8Array) await wasmPostFile(url, int8Array, 'image/jpeg');
// Multipart wasmPartAdd('photo', int8Array, 'image/jpeg', 'photo.jpg'); wasmPartAdd('metadata', metaBytes, 'application/json', null); await wasmPostMultipart(url);
Full example: link:examples/wasm/crud-example.html[].
== Native (C / C++)
Build the shared library for your target:
./gradlew linkReleaseSharedMacosArm64
./gradlew linkReleaseSharedLinuxX64
Output (e.g., macOS arm64):
Compile and link (macOS example):
The C API (NativeCApi.kt) exposes functions via @CName:
// HTTP const char* haldish_get(const char* url); const char* haldish_post_json(const char* url, const char* json); const char* haldish_patch_json(const char* url, const char* json); int haldish_delete(const char* url); const char* haldish_post_file(const char* url, int8_t* data, int size, const char* contentType);
// Last-response metadata (read after any HTTP call) int haldish_last_status(); const char* haldish_last_content_type();
// Request headers (consumed on the next HTTP call) void haldish_headers_set(const char* key, const char* value); void haldish_headers_clear();
// Multipart builder void haldish_part_add(const char* name, int8_t* data, int size, const char* contentType, const char* fileName); void haldish_parts_clear(); const char* haldish_post_multipart(const char* url);
// HAL navigation (parse body, extract links) const char* haldish_link_href(const char* body, const char* contentType, const char* rel); const char* haldish_first_embedded_self(const char* body, const char* contentType, const char* embeddedRel);
// URI template expansion const char* haldish_expand(const char* tmpl); // no variables void haldish_vars_set(const char* key, const char* value); void haldish_vars_add(const char* key, const char* value); void haldish_vars_reset(); const char* haldish_expand_vars(const char* tmpl); // expand + reset builder
Full example: link:examples/cpp/crud_example.cpp[].
== HAL Format Detection
When no Content-Type header is present, the parser sniffs the body:
|=== | Starts with | Detected format
| { or [
| JSON
| <
| XML
| anything else (non-empty) | YAML |===
Explicit content types take precedence: application/hal+json, application/json, application/hal+xml, application/xml, text/xml, application/hal+yaml, application/yaml, text/yaml.
== Building
./gradlew build
./gradlew jvmTest
== Related Tools
== License
link:https://www.apache.org/licenses/LICENSE-2.0[Apache License 2.0]