skip to content

In a JMeter JSR223 script, how do vars.put and vars.putObject differ?

level: middleimportance: should knowfreq 52%

answer

  1. One setter is typed, the other is not
  2. The map behind them is the same map
  3. Only one getter preserves the type
  4. A ${name} reference goes through the String getter

basics

~20 s

vars.put is declared put(String, String) and stores a String. vars.putObject takes any Object. Both write into the same per-thread map, and vars.getObject is the only accessor that gives the stored object back with its type.

solid answer

~40 s

`JMeterVariables` keeps one `Map<String, Object>`, and the four methods differ only in the types at the edges. `put(String key, String value)` is the String-typed setter; `putObject(String key, Object value)` accepts anything, so a `List`, a parsed document or a JDBC result set can be parked under a name. On the way out, `getObject(key)` returns the stored reference unchanged, while `get(key)` returns a `String` — in JMeter 6.0.0 it hands back the value when it is already a `String` and calls `toString()` on it otherwise. Because a `${name}` reference resolves through `get`, an object stored with `putObject` renders in a sampler field as its `toString()` text, never as the object.

code

groovy · 9 lines
groovy
// JSR223 PostProcessor (Language: groovy)
vars.put("orderId", "A-1001")             // put(String, String)
vars.putObject("orderLines", [1, 2, 3])    // putObject(String, Object)

vars.get("orderId")               // "A-1001"
vars.get("orderLines")            // "[1, 2, 3]"  - toString() of the List
vars.getObject("orderLines")[0]   // 1  - the List itself

// In the next sampler's Body Data:  ${orderLines}  ->  [1, 2, 3]

go deeper

for a junior

Recall that put is for text and putObject is for anything else, and that you read them back with get and getObject respectively. Convert numbers to a String before calling put.

for a middle

Explain the shared Map behind the four methods, and that get returns a String by calling toString on a non-String value, which is why a ${name} reference renders an object rather than passing it along.

for a senior

Judge which channel a value belongs in: text a sampler field will interpolate, or an object only another script consumes. Watch what a large object parked under a key costs each thread over a long run.

for a principal

Set the team convention for what may be parked in variables at all, so plans do not quietly carry parsed documents or open handles per thread across thousands of iterations.

`vars` is one `JMeterVariables` instance per thread, and inside it one `Map<String, Object>`. The four methods on it are just typed doors onto that map, and knowing which door you used decides what a later element can do with the value. ## The four methods | Method | Signature | What it does | |---|---|---| | `put` | `put(String key, String value)` | stores a String under the key | | `putObject` | `putObject(String key, Object value)` | stores any reference under the key | | `get` | `String get(String key)` | returns a String, or `null` if absent | | `getObject` | `Object getObject(String key)` | returns the stored reference, or `null` | There is no type checking on the way in beyond the declared parameter: `put` is simply the setter whose second parameter is a `String`, so a Groovy script holding an `Integer` or a `List` must either convert it (`id as String`) or call `putObject`. ## What `get` does with a non-String In JMeter 6.0.0, `JMeterVariables.get` reads the map and: 1. returns the value directly if it is already a `String`; 2. otherwise returns `value.toString()`; 3. otherwise returns `null` when the key is absent. So `get` never throws on a value that `putObject` stored — it renders it. That matters for the plan, because a `${name}` reference in a sampler field is compiled to a `SimpleVariable` whose `toString()` calls exactly this `get`. Store a `List` with `putObject` and a field written `${orderLines}` will send the text `[1, 2, 3]`. Useful for a log line, almost never what you wanted in a request body. ## Which one to use when handing a value to the next sampler - **The next sampler reads it as `${name}` in a field** — use `put`, and convert to a String yourself so you control the formatting rather than inheriting some class's `toString()`. - **The next element is another script that needs the real object** — use `putObject` and `getObject`. This is the channel for a parsed structure, an open handle, or a collection you will iterate. - **Both** — store the object with `putObject` and, separately, the display form with `put` under a second key. Two keys is cheaper than a fragile `toString()` contract. ## Things that bite - `getObject` on a key you wrote with `put` returns the `String` — the map does not remember which setter you used. - `remove(key)` returns the old value as an `Object`; there is no String-typed removal. - Objects parked with `putObject` stay reachable for the life of that thread's variables, so a large parsed body held under a key is retained per thread until something overwrites or removes it. Clear keys you no longer need in a long iteration. - `putAll` exists in two shapes — taking a `Map<String, ?>` or another `JMeterVariables` — and both write straight into the same map. ## Reading it back in the GUI A **Debug Sampler** with JMeter Variables enabled prints every entry, and the printed form goes through the same `toString()` path — so an entry that looks like `[1, 2, 3]` there might be a real `List` behind `getObject`, or might be that literal String. If the distinction matters, log `vars.getObject("key").getClass()` once and settle it.

  • In JMeter, what does vars.getObject return for a key that was written with vars.put?
    The `String` that was stored. `JMeterVariables` keeps a single `Map<String, Object>` and does not record which setter wrote an entry, so `getObject` simply returns whatever reference is under the key — here, a `String`.
  • Why would you store both an object and its String form under two different JMeter variable names?
    Because the two consumers differ. A later script wants the real object through `getObject`; a sampler field can only take the text that `${name}` produces. Writing the display form explicitly with `put` keeps the request body off an accidental `toString()` contract.

saying these in an interview costs you the question

  • Claims vars.get throws on a value stored with putObject
  • Thinks putObject makes an object usable inside ${name}
  • Believes the two setters use two separate maps
  • Says getObject recovers the type for a put String
  • Assumes a missing key yields an empty string, not null