Using JSON in segments
UpdatedAttributes and event data can contain nested (JSON) values—arrays, objects, and arrays of objects. You can use these nested values to match people in segments, filters, and trigger criteria. If you’re not familiar with JSON, we provide some simple options to help you traverse nested attributes. If you are familiar with JSON, you can use JSON dot notation as you normally would.
New to JSON?
JSON is a standard, simple way to organize and structure data. Check out our introduction to JSON and learn how you can take advantage of JSON in Customer.io.
How it works
Attributes and events can contain nested JSON (JavaScript Object Notation) values, and you can evaluate these values when you build a segmentA group of people who match a series of conditions. People enter and exit the segment automatically when they match or stop matching conditions., filter, or trigger condition. If you’re not familiar with JSON, you might check out our introduction to JSON—but we’ll explain a bit about how it works on this page. Read on and you’ll be a pro in no time.
If you’re new to JSON (nested attributes and values), or just want a simplified experience, you can use the Has the property or Where at least one selectors to build segments based on nested values. See the If you’re new to JSON section below for more about these selectors.
If you’re already familiar with JSON, you can use dot notation to point to nested values. You might use the Advanced option to point right to the nested value you want to evaluate.
You can only use JSON customer attributes and event properties in segment conditions
You can’t use nested device attributes, collection properties, or trigger properties (from an incoming webhook or an API-triggered broadcast) in segment conditions.
Example data
On this page, we’ll consider the following sample JSON data representing a complex attribute or event data. We’ll use it to demonstrate segment matches on this page. In both cases, we’ll call it shopper_history
.
{
"last_shopped": 1662587234,
"location": {"city": "Montreal","province": "QC"},
"purchases": [
{
"id": 123,
"type": "computers",
"name": "Monitor",
"price": 25,
"discount": 10,
"shipping_address": {"city": "Calgary","province": "AB"},
"coupons_applied": [
{
"coupon_code": "AXXXXX",
"discount": "10%"
},
{
"coupon_code": "BXXXXX",
"discount": "15%"
}
]
},
{
"id": 456,
"type": "computers",
"name": "Mouse",
"price": 15,
"shipping_address": {"city": "Edmonton","province": "AB"}
}
],
"stores_visited": ["Winnipeg","Toronto","Vancouver"],
"coupons_received": [5,10,20],
"children_ages": [1654099180,1654099181,1654099182],
"lottery_tickets":
[
[1,3,5,7,9],
[1,2,3,5,8]
]
}
If you’re new to JSON…
We nest values under parents in two different formats: objects and arrays.
See the curly brackets surrounding the data above—the {
on the first line and the }
on the last line? That’s called an object. If we stored all of the data above in an attribute called shopper_history, you could filter people who last_shopped
with you using the Has the property selector—because last_shopped
resides within the shopper_history
attribute.
See the square brackets surrounding the value after stores_visited
above? That’s called an array. If we wanted to build a segment of all people who visited the Toronto store, we could build a segment of people with a shopper_history attribute that has the property stores_visited
where at least one value is Toronto
.
An array can also contain objects—that’s what’s going on with purchases
above. If we wanted to find everybody who used a coupon on their purchase, we could set up a segment where: shopper_history
has the property purchases
where at least one property called coupons_applied
exists.
Nested properties in the basic segment builder
The segment builder has two selectors that help you traverse complex values:
- Has the property: Checks for a child of an object.
- Where at least one: Checks for a matching value in an array.
When working with complex attributes and event values, you might want to open a representative person in another browser window—someone with data that you can reference as you build your segment.
Has the property (objects)
The has the property selector lets you traverse objects. Using our example data, you could create a condition stating Attribute location
has the property city
equal to Montreal
. This evaluates to true. When you use the has the property selector, we nest child values, helping show the parent-child relationships between nested attributes.
At least one of (arrays)
The at least one of selector matches an item in an array. When you use it, you select whether you want to match a property or array value in the array.
- property: your array contains objects—like
purchases
in our example data, and you want to traverse that data to find a match. - array value: your array only contains values, like
stores_visited
in our example data. Use this to match literally to any value in the array.
For a simple array—a list of values in square brackets—simply use the contains
or equals
operators to match a value. Using the stores_visited
array from our shopper_history
example data, the following would evaluate to true.
shopper_history.stores_visited contains Winnipeg
is true, and the person represented by the data above would join the segment.
You cannot specify a position or “index” when you use the segment builder this way. If you want to match a value at a specific index, you’ll need to use JSON dot notation in the advanced editor.
Equals and Contains have some minor differences
The equals operator searches for a matching value, and contains searches for a match within a value in the array. See our section below for more information.
Dot notation in the basic editor
You can use dot notation in the simplified, basic editor. However, you can’t specify an array index when you use the editor this way.
For example, if you wanted to filter or segment on a matching value in the stores_visited
array, you could use Attribute shopper_history.stores_visited[]
is equal to Winnipeg
.
But you’d have to switch to the advanced editor if you only wanted to match the first item in the array—i.e. shopper_history.stores_visited[0]
.
The Advanced Editor
You can click Advanced to switch to an expanded JSON dot notation editor.
If you don’t provide an array index when you use dot notation, we’ll match any item in the array (e.g. array[]
). If you provide an index, we’ll match a specific position in an array. Arrays are zero-indexed—e.g. array[0]
matches the first item in the array.
Using our stores_visited
example:
shopper_history.stores_visited[] contains Toronto
is trueshopper_history.stores_visited[0] contains Toronto
is false
This works with nested arrays and arrays of objects as well. For example if we wanted to create a segment of people who used a specific coupon, we might use shopper_history.purchases[].coupons_applied[].coupon_code
. This would search against every object within purchases
for an array called coupons_applied
, and then for the coupon_code
for every object within coupons_applied
.
Empty brackets don’t work in Liquid
You can’t use our empty bracket array syntax (array[]
) to match any item in an array using liquidA syntax that supports variables, letting you personalize messages for your audience. For example, if you want to reference a person’s first name, you might use the variable {{customer.first_name}}
.. Instead, you’ll need to use a for loop or provide a specific index in the array.
The ?
operator: for arrays or objects
If you’re not sure whether an attribute is an object or an array, you can use the ?
operator in place of square brackets.
When you use a ?
, we’ll treat a value first as an array; if we don’t find a match, we’ll treat the value as an object. For example, if you wanted to access a key in purchases, but you weren’t sure if it was an object or an array of objects, you could use: shopper_history.purchases?.type
.
If you use the where at least one property in the basic editor and then switch to Advanced, you’ll see that we use the ?
operator rather than square brackets, segments aren’t aware of your attribute and event data structure until you save conditions and we begin processing the segment.
Traversing nested attributes
When you search within an object or an array, we’ll traverse the entire value until we find a match. For example, purchases[].coupons_applied[].coupon_code
searches for the coupon_code
key within any coupons_applied
found in purchases
.
However, if you know your coupon code values are unique, you could also simply search purchases[]
for your coupon code value. We’ll automatically search for a matching value in any child property of the purchases[]
array.
Equals vs contains in an array
In most cases, the equals and contains conditions are functionally identical when you use the at least one of selector (e.g. you reference an array). But there is a minor difference: Equals searches for an exact match. Contains searches for a value containing a string value—so a partial string can match.
Using our stores_visited
example data, these two statements are identical and true:
stores_visited[] contains Toronto
stores_visited[] equals Toronto
But these two statements are not identical:
stores_visited[] contains Toro
is truestores_visited[] equals Toro
is false
If you don’t use array syntax—either the where at least one selector or []
—contains
will work but equals
will not. When you don’t use array syntax, we treat the value as a string; the stringified value of stores_visited
contains Toronto
, but it has more characters than just that word, so it can’t equal Toronto
.
stores_visited contains Toronto
is truestores_visited equals Toronto
is false
Use does not exist instead of an empty string
In general, you shouldn’t send attributes or event data properties with empty strings. It’s not necessarily a problem if you do, but we don’t store customer attributesA key-value pair that you associate with a person or an object—like a person’s name, the date they were created in your workspace, or a company’s billing date etc. Use attributes to target people and personalize messages. Attributes are analogous to traits in Data Pipelines. with empty values, and you can’t save a segment or filter condition with an empty value.
But, if you do pass us attributes or properties with empty values—like in event data—you can segment or filter against these conditions using the exists or does not exist operators.