跳到主要内容

Object Paths

An ObjectPath names one field of the object Aethrix builds for a service operation. It is the address you enter when a step has to read or write a specific field rather than the whole message: a mapping rule, a data transfer, an assertion, or a condition in a simulated service.

Where paths are used​

WindowField or command
Send Request step, mapping rulesMapping Rules → Add Mapping Rule, or right-click a field in the object tree → Add mapping rule here
Data Transfer stepAdd/Edit Data Transfer Rule, with Path Type set to PropertyValue and the path in Path
Assertions on a responseAdd Object Path Assertion
Simulated service rulesConditions → Edit Condition...
API Playground checksthe Assertions tab, the object-path kind

The same syntax applies in all of them and on every protocol: REST, SOAP, GraphQL, gRPC, Kafka and OData. Some windows label it PropertyValue rather than Object Path; both names refer to this addressing.

The root of a path​

A path is written against the operation's request object, or its response object. That object holds the parts of the message, and the body is one of them. The object tree shows the same structure:

POST_Create_a_booking_The_ba…
└─ Body
└─ flight
├─ carrier HB
└─ number 118

The carrier of that flight is Body.flight.carrier. Body is a node in the path, not a prefix the syntax requires.

The remaining parts of the message are siblings of Body and are addressed directly, by the name the message uses:

Part of the messagePathTaken from
Body of a request or responseBody.flight.carrierthe message content
Path parameterreferencePOST /api/v1/bookings/{reference}/baggage
Query fielddateGET /api/v1/flights?from=...&to=...&date=...
HeaderX-Correlation-Idthe header name, with its capitals and dashes

An operation that returns a list as its body is read from the response object in the same way: Body[0].number.

Copying a path​

Right-click a field in the object tree and choose Copy Object Path. This is the reliable way to obtain a path for a field whose name is long, mixed-case, or spelled in a form you would not type by hand. The tree is the body editor in the Playground and on a Send Request step, the response editor, the simulation response editor, and the Kafka message payload editor.

Syntax​

Segments are separated by dots, and each segment names one field. A segment that names a collection may carry a selector in square brackets. There are no wildcards, filters or functions, and matching is case-sensitive: Body.photos[1].Name is not Body.photos[1].name.

Body.passengers[1].bags[type:checked].weightKg

Read left to right: the body of the request, its second passenger, that passenger's checked bag, and the weight of that bag.

Selecting an element of a collection​

PathMeaning
Body.photos[0]the first photo. Positions start at 0
Body.photosthe whole collection. A path that ends at a collection selects it, which is how an assertion or a data transfer takes a list as one value
Body.photos.namean error. A path that continues past a collection has to name the element it means

Two further rules:

  • Writing through an index past the end grows the collection. Body.passengers[4].name = "Jo" on a booking with two passengers creates the elements up to position 4.
  • A selector on a field that holds no elements is refused, rather than applied to the field as a whole.

Selecting an element of a polymorphic collection​

Some collections hold items of different kinds, where each item declares which kind it is. In JSON the item carries a field for this, commonly type:

"bags": [
{ "type": "checked", "weightKg": 23 },
{ "type": "carryOn", "weightKg": 7 }
]

Address those items by the value they carry:

Body.bags[type:checked].weightKg
Body.bags[type:carryOn].hasLaptop

checked and carryOn are the values from the message. On an XML or SOAP service the equivalent is the element name the schema gives each kind, which is also the name the object tree shows, so the path you copied is the path that works: Body.bags[type:hold].

Prefer this to a position where the kind matters. A position records the order one payload happens to be in; if the service returns the carry-on first, or a bag is added at the front, bags[0] addresses a different item, while bags[type:checked] still addresses the checked bag.

SituationBehaviour
Several items carry the same tagthe path addresses the first of them. Use a position for a specific one: Body.bags[1]
No item carries the tag, and the service declares itreading returns nothing; writing creates that item, so an optional part of a payload can be filled without restating the whole message
The tag is not one the service declaresthe path is refused, and the error lists the tags that are declared
Two declared kinds share one tag namethe path is refused and names both kinds. Address the item by position; the definition needs a proper tag mapping

To create such an item in the editor, select the entry in the object tree, choose Initialize as …, then pick the kind. Append Item adds another entry.

Example​

Baggage registration for one booking. The object tree shows the path parameter and the body as siblings:

POST_Register_ONE_piece_of_ba…
├─ reference GZT001
└─ Body
└─ bags
├─ type checked weightKg 23 lengthCm 75
└─ type carryOn weightKg 7 hasLaptop true
TargetPath
The booking reference in the URLreference
The weight of the first bagBody.bags[0].weightKg
The weight of the checked bagBody.bags[type:checked].weightKg
Whether a laptop is in the cabin bagBody.bags[type:carryOn].hasLaptop
The whole list of bags, as one valueBody.bags
A checked bag on a booking that has noneBody.bags[type:checked].weightKg = 20, which creates the item

On the response side, an assertion of the object-path kind reads Body.bags[type:checked].weightKg from the compiled response and compares it with the expected value. A Data Transfer step with Path Type PropertyValue and Path Body.bags[type:carryOn].tagId stores that tag in a variable, which a later step's mapping rule can write to Body.bags[type:checked].matchesTag.

Error messages​

The message saysCauseRemedy
... is not found on ..., followed by a list of namesa segment names no field at that positioncopy the path from the tree; check capitalisation, and check that the path came from the step that runs the rule
... walks through '<field>', a collection ... without saying which elementthe path continues past a collection with no selectoradd [0] or [type:…], or end the path at the collection to take it whole
[type:'<tag>'] names no variant of '<field>'. The values it does declare: …the tag belongs to no kind this collection can holduse one of the listed tags
[type:'<tag>'] is ambiguoustwo declared kinds share the tagaddress the item by position
... carries an index, but '<field>' is a … which holds no elementsa selector was applied to a single fieldremove the selector

Scope​

A path is read against the object of one step, and the same field can sit at a different depth elsewhere. A test step's request is wrapped in an envelope, so its fields sit below a step-specific prefix, while the Playground and a saved request expose the parts of the message directly. A path that works in one of those may need a prefix, or lose one, in the other. Take the path for a mapping rule from the step that will run the rule.

Not supported​

  • Keyed maps. A field defined as "any key with a value" cannot be addressed key by key: Body.extras.someKey fails. Edit that region as text, or declare the keys in the definition.
  • Arrays of arrays. Body.matrix[0][1] does not select the second item of the first inner list; the second bracket is not applied. Use a script step for this shape.
  • Regions kept as text. Where a definition is too open for Aethrix to build a field tree, that part of the message is shown and edited as text, and has no paths.
  • Fields outside the definition. A path cannot name a field the service description does not declare, even if a live payload contains it.
  • Operations with no response model. An object-path assertion on such an operation reports that there is no model to read, because the transport returns the raw message rather than an object.