Response Mapping
Transforming JSON API responses into RDF triples using RML mappings.
When a service integration action calls an external API, the response comes back as JSON. Response mappings transform that JSON into RDF triples that RARS can reason about. They use RML (RDF Mapping Language) and live in mappings.ttl. This guide walks through building response mappings for common API patterns, from simple flat arrays to nested objects and computed values.
The Basic Pattern
Every response mapping answers three questions: what part of the response to iterate over, what URI to give each result, and what properties to extract.
The API returns:
[
{
"id": "123",
"title": "Review Q3",
"status": "open",
"assignee_id": "alice"
},
{
"id": "456",
"title": "Ship v2",
"status": "in-progress",
"assignee_id": "bob"
}
]Define the individual property mappings:
tasks:pom_externalId
a rml:PredicateObjectMap ;
rml:predicate tasks:externalId ;
rml:objectMap [ rml:reference "id" ; rml:datatype xsd:string ] .
tasks:pom_title
a rml:PredicateObjectMap ;
rml:predicate tasks:title ;
rml:objectMap [ rml:reference "title" ; rml:datatype xsd:string ] .
tasks:pom_status
a rml:PredicateObjectMap ;
rml:predicate tasks:status ;
rml:objectMap [ rml:reference "status" ; rml:datatype xsd:string ] .
tasks:pom_assigneeId
a rml:PredicateObjectMap ;
rml:predicate tasks:assigneeId ;
rml:objectMap [ rml:reference "assignee_id" ; rml:datatype xsd:string ] .Then compose them into the triples map:
tasks:TaskResponseMap
a rml:TriplesMap ;
rml:logicalSource [
rml:referenceFormulation rml:JSONPath ;
rml:iterator "$[*]"
] ;
rml:subjectMap [
rml:template "https://example.org/tasks/{id}" ;
rml:class tasks:Task
] ;
rml:predicateObjectMap tasks:pom_externalId ;
rml:predicateObjectMap tasks:pom_title ;
rml:predicateObjectMap tasks:pom_status ;
rml:predicateObjectMap tasks:pom_assigneeId .This produces:
<https://example.org/tasks/123>
a tasks:Task ;
tasks:externalId "123" ;
tasks:title "Review Q3" ;
tasks:status "open" ;
tasks:assigneeId "alice" .
<https://example.org/tasks/456>
a tasks:Task ;
tasks:externalId "456" ;
tasks:title "Ship v2" ;
tasks:status "in-progress" ;
tasks:assigneeId "bob" .Named property mappings can be shared across multiple response maps. If your "list tasks" and "get task" endpoints return the same fields, the same POMs work for both.
Reusing POMs Across Response Maps
A "get single task" endpoint returns the same fields. Reuse the POMs with a different iterator:
tasks:SingleTaskResponseMap
a rml:TriplesMap ;
rml:logicalSource [
rml:referenceFormulation rml:JSONPath ;
rml:iterator "$"
] ;
rml:subjectMap [
rml:template "https://example.org/tasks/{id}" ;
rml:class tasks:Task
] ;
rml:predicateObjectMap tasks:pom_externalId ;
rml:predicateObjectMap tasks:pom_title ;
rml:predicateObjectMap tasks:pom_status ;
rml:predicateObjectMap tasks:pom_assigneeId .Same POMs, different logical source ("$" for single object vs "$[*]" for array). The property mappings are defined once.
Handling Nested Responses
Many APIs wrap their results in a container object. Slack returns {"ok": true, "channels": [...]}. GitHub returns {"items": [...]}. Adjust the iterator to point at the actual data:
channels:pom_name
a rml:PredicateObjectMap ;
rml:predicate channels:name ;
rml:objectMap [ rml:reference "name" ; rml:datatype xsd:string ] .
channels:pom_topic
a rml:PredicateObjectMap ;
rml:predicate channels:topic ;
rml:objectMap [ rml:reference "topic.value" ; rml:datatype xsd:string ] .
channels:ChannelResponseMap
a rml:TriplesMap ;
rml:logicalSource [
rml:referenceFormulation rml:JSONPath ;
rml:iterator "$.channels[*]"
] ;
rml:subjectMap [
rml:template "https://example.org/channels/{id}" ;
rml:class channels:Channel
] ;
rml:predicateObjectMap channels:pom_name ;
rml:predicateObjectMap channels:pom_topic .Notice "topic.value" in the reference: you can navigate nested JSON paths with dot notation to reach properties inside nested objects.
URI Template Design
The rml:template in the subject map determines the URI for each mapped resource. This URI is the resource's identity in your graph. Get it right:
Use the external system's stable ID. The id field from the API is ideal. If the API uses UUIDs, slugs, or numeric IDs, use them. Avoid fields that can change (like name or status).
Namespace under your matrix. Keep mapped resources in your domain namespace:
rml:template "https://example.org/tasks/{id}" ;
rml:template "https://example.org/tasks/project/{project_id}" .Be consistent across mappings. If your "list tasks" and "get task" endpoints both return task data, they should produce the same URI for the same task. This way RARS recognizes them as the same resource regardless of which action retrieved them. Shared POMs and consistent URI templates make this automatic.
Transforming Values with GREL Functions
Sometimes the raw API values need transformation before they become useful RDF properties. You can apply GREL functionsGREL functions within your POMs. Sometimes the raw API values need transformation before they become useful RDF properties. You can apply GREL functions within your POMs.
Normalizing Case
An API returns statuses in ALL CAPS but your ontology uses lowercase. Define the POM with a function map:
tasks:pom_status_normalized
a rml:PredicateObjectMap ;
rml:predicate tasks:status ;
rml:objectMap [
rml:reference "status" ;
rml:datatype xsd:string ;
rml:functionMap [
rars-os:executesFunction grel:toLowerCase ;
rars-os:functionInput [
rars-os:functionParameter grel:valueParameter ;
rars-os:functionInputValue "status"
]
]
] .Use tasks:pom_status_normalized instead of tasks:pom_status in your triples map to get lowercase values.
Replacing Characters
An API uses underscores in status values (in_progress) but your ontology uses hyphens (in-progress):
tasks:pom_status_hyphenated
a rml:PredicateObjectMap ;
rml:predicate tasks:status ;
rml:objectMap [
rml:reference "status" ;
rml:datatype xsd:string ;
rml:functionMap [
rars-os:executesFunction grel:string_replace ;
rars-os:functionInput [
rars-os:functionParameter grel:valueParameter ;
rars-os:functionInputValue "status"
] ;
rars-os:functionInput [
rars-os:functionParameter grel:p_string_find ;
rars-os:functionInputValue "_"
] ;
rars-os:functionInput [
rars-os:functionParameter grel:p_string_replace ;
rars-os:functionInputValue "-"
]
]
] .Joining Arrays
An API returns tags as a JSON array (["urgent", "frontend"]) but your ontology stores them as a comma-separated string:
tasks:pom_tags
a rml:PredicateObjectMap ;
rml:predicate tasks:tags ;
rml:objectMap [
rml:reference "tags" ;
rml:datatype xsd:string ;
rml:functionMap [
rars-os:executesFunction grel:array_join ;
rars-os:functionInput [
rars-os:functionParameter grel:valueParameter ;
rars-os:functionInputValue "tags"
] ;
rars-os:functionInput [
rars-os:functionParameter grel:p_string_sep ;
rars-os:functionInputValue ", "
]
]
] .For the complete list of available functions, see the GREL function referenceGREL function reference.
Mapping Design Principles
Map What You Need, Not Everything
External APIs often return 30+ fields per object. Only map the fields your domain actually uses. Every mapped property adds to the graph and increases the context RARS works with. If you don't use created_by_email anywhere in your ontology or actions, don't map it.
Match Your Ontology Types
The rml:datatype on each object map should match the rdfs:range on the corresponding property in your ontology. If your ontology says tasks:title has range xsd:string, the mapping should use rml:datatype xsd:string. Mismatches cause subtle constraint violations.
Naming Conventions
When using named predicate object maps, a consistent naming convention helps readability:
tasks:pom_title # maps the title field
tasks:pom_status # maps the status field (raw)
tasks:pom_status_lower # maps the status field (lowercased)
tasks:pom_assigneeId # maps the assignee_id fieldSee Also
- Request Mapping: the outbound direction, constructing HTTP requests from graph data
- Building Integrations: end-to-end service integration walkthrough
- RDF Functions: GREL and other function types used in mappings - GREL Function ReferenceGREL Function Reference: complete parameter details for all available functions
- RML Specification: the RDF Mapping Language standard