Search code examples
jsonjmeterfastjson

JMeter failed to keep JSON order by using fastjson JSONObject.parseObject


Backgroud:

In Jmeter, I need to get the json post data, and then parse to the JSONObject

But the key order should the same

The json post data is: { "one": "1", "two": "2", "three": "3", "four": "4" }

After JSON Object parse, it also should be: { "one": "1", "two": "2", "three": "3", "four": "4" }

Solution:

In pure java, I could use the following code to keep the order

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;

String requestBody = "{ \"one\": \"1\", \"two\": \"2\", \"three\": \"3\", \"four\": \"4\" }";
log.info("********************requestBody: " + requestBody);

// Expected: keep the JSONObject key oder same as the body data
// But following code not working in JMeter JSR223 Sampler, it works fine in pure JAVA
JSONObject maps = JSONObject.parseObject(requestBody, Feature.OrderedField);
log.info("********************maps expected: " + maps);

Issue:

But the above code is not working in JMETER JSR223 Sampler, I get the following error:

Static method parseObject( java.lang.String, com.alibaba.fastjson.parser.Feature ) not found in class'com.alibaba.fastjson.JSONObject'...

Reproduce:

Save the following code as .jmx file to reproduce in JMeter

You need to download the fastjson 1.2.83, and put to the jmeter lib folder: https://mvnrepository.com/artifact/com.alibaba/fastjson/1.2.83

<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.5">
  <hashTree>
    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
      <stringProp name="TestPlan.comments"></stringProp>
      <boolProp name="TestPlan.functional_mode">false</boolProp>
      <boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>
      <boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
      <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
        <collectionProp name="Arguments.arguments"/>
      </elementProp>
      <stringProp name="TestPlan.user_define_classpath"></stringProp>
    </TestPlan>
    <hashTree>
      <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
        <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
        <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
          <boolProp name="LoopController.continue_forever">false</boolProp>
          <stringProp name="LoopController.loops">1</stringProp>
        </elementProp>
        <stringProp name="ThreadGroup.num_threads">1</stringProp>
        <stringProp name="ThreadGroup.ramp_time">1</stringProp>
        <boolProp name="ThreadGroup.scheduler">false</boolProp>
        <stringProp name="ThreadGroup.duration"></stringProp>
        <stringProp name="ThreadGroup.delay"></stringProp>
        <boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
      </ThreadGroup>
      <hashTree>
        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="JSON Sort Issue" enabled="true">
          <boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
          <elementProp name="HTTPsampler.Arguments" elementType="Arguments">
            <collectionProp name="Arguments.arguments">
              <elementProp name="" elementType="HTTPArgument">
                <boolProp name="HTTPArgument.always_encode">false</boolProp>
                <stringProp name="Argument.value">{&#xd;
  &quot;one&quot;: &quot;1&quot;,&#xd;
  &quot;two&quot;: &quot;2&quot;,&#xd;
  &quot;three&quot;: &quot;3&quot;,&#xd;
  &quot;four&quot;: &quot;4&quot;&#xd;
}</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
              </elementProp>
            </collectionProp>
          </elementProp>
          <stringProp name="HTTPSampler.domain">httpbin.org</stringProp>
          <stringProp name="HTTPSampler.port"></stringProp>
          <stringProp name="HTTPSampler.protocol">https</stringProp>
          <stringProp name="HTTPSampler.contentEncoding"></stringProp>
          <stringProp name="HTTPSampler.path">/post</stringProp>
          <stringProp name="HTTPSampler.method">POST</stringProp>
          <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
          <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
          <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
          <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
          <stringProp name="HTTPSampler.response_timeout"></stringProp>
        </HTTPSamplerProxy>
        <hashTree>
          <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="JSR223 预处理程序" enabled="true">
            <stringProp name="cacheKey">true</stringProp>
            <stringProp name="filename"></stringProp>
            <stringProp name="parameters"></stringProp>
            <stringProp name="script">import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;

String requestBody = sampler.getArguments().getArgument(0).getValue();
log.info(&quot;********************requestBody: &quot; + requestBody);

// UnExpected: key order is not same as the body data
JSONObject maps = JSONObject.parseObject(requestBody);
log.info(&quot;********************maps unexpected: &quot; + maps);

// Expected: keep the JSONObject key oder same as the body data
// But following code not working in JMeter JSR223 Sampler, it works fine in pure JAVA
JSONObject maps = JSONObject.parseObject(requestBody, Feature.OrderedField);
log.info(&quot;********************maps expected: &quot; + maps);
</stringProp>
            <stringProp name="scriptLanguage">beanshell</stringProp>
          </JSR223PreProcessor>
          <hashTree/>
        </hashTree>
      </hashTree>
    </hashTree>
  </hashTree>
</jmeterTestPlan>


Solution

  • First of all, you declare your maps variable twice in the same scope, this is not valid code at all.

    Coming back to your question: just change language to groovy, it will resolve your issue.

    enter image description here

    Since JMeter 3.1 it's recommended to use Groovy language for scripting mainly because Groovy performance is much better comparing the other scripting options. Moreover Groovy supports Java better and Beanshell doesn't support any Java 5+ feature.