This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 4861336aaf4f CAMEL-26267: camel-huaweicloud-iam - read the JSON of 
updateUser and updateGroup from bytes, a stream or a file, and write the 
results with the field names of the IAM API (#28966)
4861336aaf4f is described below

commit 4861336aaf4f7b5ce58d07183484b6d4b1697382
Author: Andrea Cosentino <[email protected]>
AuthorDate: Mon Nov 2 22:55:47 2026 +0100

    CAMEL-26267: camel-huaweicloud-iam - read the JSON of updateUser and 
updateGroup from bytes, a stream or a file, and write the results with the 
field names of the IAM API (#28966)
    
    * CAMEL-26267: camel-huaweicloud-iam - read the JSON of updateUser and 
updateGroup from bytes, a stream or a file, and write the results with the 
field names of the IAM API
    
    * CAMEL-26267: camel-huaweicloud-iam - name the field of a wrong value in 
the error, and test the field names of the getGroupUsers and updateUser results
    
    Signed-off-by: Andrea Cosentino <[email protected]>
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 .../camel/catalog/components/hwcloud-iam.json      |   4 +-
 .../camel/catalog/docs/hwcloud-iam-component.adoc  |  31 +-
 .../camel-huawei/camel-huaweicloud-iam/pom.xml     |   5 -
 .../component/huaweicloud/iam/hwcloud-iam.json     |   4 +-
 .../src/main/docs/hwcloud-iam-component.adoc       |  31 +-
 .../component/huaweicloud/iam/IAMEndpoint.java     |  18 +-
 .../component/huaweicloud/iam/IAMProducer.java     | 141 +++++---
 .../component/huaweicloud/iam/GetUserTest.java     |   2 +-
 .../huaweicloud/iam/IAMProducerJsonTest.java       | 371 +++++++++++++++++++++
 .../component/huaweicloud/iam/ListUsersTest.java   |   2 +-
 .../huaweicloud/iam/UpdateGroupJsonTest.java       |   2 +-
 .../component/huaweicloud/iam/UpdateGroupTest.java |   2 +-
 .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc    |  12 +
 13 files changed, 546 insertions(+), 79 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/hwcloud-iam.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/hwcloud-iam.json
index b8f04f244e6f..a54261ff3932 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/hwcloud-iam.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/hwcloud-iam.json
@@ -28,8 +28,8 @@
     "autowiredEnabled": { "index": 1, "kind": "property", "displayName": 
"Autowired Enabled", "group": "advanced", "label": "advanced", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, "description": 
"Whether autowiring is enabled. This is used for automatic autowiring options 
(the option must be marked as autowired) by looking up in the registry to find 
if there is a single instance of matching t [...]
   },
   "bodyTypes": {
-    "producerIn": { "carriers": [ "any" ], "byOption": [ { "option": 
"operation", "value": "updateUser", "carriers": [ "text", "pojo" ] }, { 
"option": "operation", "value": "updateGroup", "carriers": [ "text", "pojo" ] } 
], "description": "Not used by the list and get operations, which take the ids 
from the options or the CamelHwCloudIamUserId and CamelHwCloudIamGroupId 
exchange properties. updateUser needs an UpdateUserOption of the Huawei Cloud 
SDK or its JSON as text, and updateGroup  [...]
-    "producerOut": { "carriers": [ "text" ], "javaType": "java.lang.String", 
"format": "json", "description": "The JSON of the result as text: the array of 
the users or the groups for listUsers, listGroups and getGroupUsers, the user 
or the group for getUser, updateUser and updateGroup. The keys are the field 
names of the Huawei Cloud SDK, such as domainId" }
+    "producerIn": { "carriers": [ "any" ], "byOption": [ { "option": 
"operation", "value": "updateUser", "carriers": [ "text", "bytes", "stream", 
"file", "pojo" ] }, { "option": "operation", "value": "updateGroup", 
"carriers": [ "text", "bytes", "stream", "file", "pojo" ] } ], "description": 
"Not used by the list and get operations, which take the ids from the options 
or the CamelHwCloudIamUserId and CamelHwCloudIamGroupId exchange properties. 
updateUser needs an UpdateUserOption of the  [...]
+    "producerOut": { "carriers": [ "text" ], "javaType": "java.lang.String", 
"format": "json", "description": "The JSON of the result as text: the array of 
the users or the groups for listUsers, listGroups and getGroupUsers, the user 
or the group for getUser, updateUser and updateGroup. The keys are the field 
names of the IAM API, such as domain_id, as in the JSON of the request" }
   },
   "properties": {
     "operation": { "index": 0, "kind": "path", "displayName": "Operation", 
"group": "producer", "label": "producer", "required": true, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Operation to be performed" 
},
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/hwcloud-iam-component.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/hwcloud-iam-component.adoc
index 6a160eea888d..14361fd49625 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/hwcloud-iam-component.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/hwcloud-iam-component.adoc
@@ -71,14 +71,14 @@ If any of the above properties are set, they will override 
their corresponding q
 === Passing Options Through Exchange Body
 There are many options that can be submitted to 
https://support.huaweicloud.com/en-us/api-iam/iam_08_0011.html[update a user] 
(Table 4) or to 
https://support.huaweicloud.com/en-us/api-iam/iam_09_0004.html[update a group] 
(Table 4). Since there are multiple user/group options, they must be passed 
through the exchange body.
 
-For the `updateUser` operation, you can pass the user options as an 
UpdateUserOption object or a Json string:
+For the `updateUser` operation, you can pass the user options as an 
UpdateUserOption object or its JSON, as text, bytes, a stream or a file:
 
 ._Java-only: requires SDK UpdateUserOption builder_
 
 [source,java]
 ----
 from("direct:triggerRoute")
- .setBody(new 
UpdateUserOption().withName("user").withDescription("employee").withEmail("[email protected]"))
+ .setBody(constant(new 
UpdateUserOption().withName("user").withDescription("employee").withEmail("[email protected]")))
  
.to("hwcloud-iam:updateUser?userId=********&region=cn-north-4&accessKey=********&secretKey=********")
 ----
 
@@ -126,15 +126,15 @@ YAML::
 ----
 ====
 
-For the `updateGroup` operation, you can pass the group options as a 
KeystoneUpdateGroupOption object or a Json string:
+For the `updateGroup` operation, you can pass the group options as a 
KeystoneUpdateGroupOption object or its JSON, as text, bytes, a stream or a 
file:
 
 ._Java-only: requires SDK KeystoneUpdateGroupOption builder_
 
 [source,java]
 ----
 from("direct:triggerRoute")
- .setBody(new 
KeystoneUpdateGroupOption().withName("group").withDescription("employees").withDomainId("1234"))
- 
.to("hwcloud-iam:updateUser?groupId=********&region=cn-north-4&accessKey=********&secretKey=********")
+ .setBody(constant(new 
KeystoneUpdateGroupOption().withName("group").withDescription("employees").withDomainId("1234")))
+ 
.to("hwcloud-iam:updateGroup?groupId=********&region=cn-north-4&accessKey=********&secretKey=********")
 ----
 
 [tabs]
@@ -145,7 +145,7 @@ Java::
 ----
 from("direct:triggerRoute")
  
.setBody(constant("{\"name\":\"group\",\"description\":\"employees\",\"domain_id\":\"1234\"}"))
- 
.to("hwcloud-iam:updateUser?groupId=********&region=cn-north-4&accessKey=********&secretKey=********")
+ 
.to("hwcloud-iam:updateGroup?groupId=********&region=cn-north-4&accessKey=********&secretKey=********")
 ----
 
 XML::
@@ -157,7 +157,7 @@ XML::
   <setBody>
     
<constant>{"name":"group","description":"employees","domain_id":"1234"}</constant>
   </setBody>
-  <to 
uri="hwcloud-iam:updateUser?groupId=********&amp;region=cn-north-4&amp;accessKey=********&amp;secretKey=********"/>
+  <to 
uri="hwcloud-iam:updateGroup?groupId=********&amp;region=cn-north-4&amp;accessKey=********&amp;secretKey=********"/>
 </route>
 ----
 
@@ -172,7 +172,7 @@ YAML::
         - setBody:
             constant: 
'{"name":"group","description":"employees","domain_id":"1234"}'
         - to:
-            uri: hwcloud-iam:updateUser
+            uri: hwcloud-iam:updateGroup
             parameters:
               groupId: "********"
               region: cn-north-4
@@ -181,17 +181,28 @@ YAML::
 ----
 ====
 
+Another body fails the exchange with an `IllegalArgumentException`.
+
+=== JSON of the Results
+
+The operations set the exchange body to the JSON of their result, as text: the 
array of the users for `listUsers` and `getGroupUsers`, the array of the groups 
for `listGroups`, the user for `getUser` and `updateUser`, and the group for 
`updateGroup`. The keys are the field names of the IAM API, as in the JSON of 
the requests above, for example the result of `getUser`:
+
+[source,json]
+----
+{"domain_id":"1234","name":"user","email":"[email protected]","pwd_status":true}
+----
+
 === Using ServiceKey Configuration Bean
 
 Access key and secret keys are required to authenticate against cloud IAM 
service. You can avoid having them being exposed
-and scattered over in your endpoint uri by wrapping them inside a bean of 
class `org.apache.camel.component.huaweicloud.iam.models.ServiceKeys`.
+and scattered over in your endpoint uri by wrapping them inside a bean of 
class `org.apache.camel.component.huaweicloud.common.models.ServiceKeys`.
 Add it to the registry and let Camel look it up by referring the object via 
endpoint query parameter `serviceKeys`.
 
 Check the following code snippets:
 
 [source,xml]
 ----
-<bean id="myServiceKeyConfig" 
class="org.apache.camel.component.huaweicloud.iam.models.ServiceKeys">
+<bean id="myServiceKeyConfig" 
class="org.apache.camel.component.huaweicloud.common.models.ServiceKeys">
    <property name="accessKey" value="your_access_key" />
    <property name="secretKey" value="your_secret_key" />
 </bean>
diff --git a/components/camel-huawei/camel-huaweicloud-iam/pom.xml 
b/components/camel-huawei/camel-huaweicloud-iam/pom.xml
index d26ad87454fe..d04ab5a8fe48 100644
--- a/components/camel-huawei/camel-huaweicloud-iam/pom.xml
+++ b/components/camel-huawei/camel-huaweicloud-iam/pom.xml
@@ -53,11 +53,6 @@
       <version>${huaweicloud-sdk-version}</version>
     </dependency>
 
-    <dependency>
-      <groupId>com.google.code.gson</groupId>
-      <artifactId>gson</artifactId>
-    </dependency>
-
     <!-- testing -->
     <dependency>
       <groupId>org.apache.camel</groupId>
diff --git 
a/components/camel-huawei/camel-huaweicloud-iam/src/generated/resources/META-INF/org/apache/camel/component/huaweicloud/iam/hwcloud-iam.json
 
b/components/camel-huawei/camel-huaweicloud-iam/src/generated/resources/META-INF/org/apache/camel/component/huaweicloud/iam/hwcloud-iam.json
index b8f04f244e6f..a54261ff3932 100644
--- 
a/components/camel-huawei/camel-huaweicloud-iam/src/generated/resources/META-INF/org/apache/camel/component/huaweicloud/iam/hwcloud-iam.json
+++ 
b/components/camel-huawei/camel-huaweicloud-iam/src/generated/resources/META-INF/org/apache/camel/component/huaweicloud/iam/hwcloud-iam.json
@@ -28,8 +28,8 @@
     "autowiredEnabled": { "index": 1, "kind": "property", "displayName": 
"Autowired Enabled", "group": "advanced", "label": "advanced", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, "description": 
"Whether autowiring is enabled. This is used for automatic autowiring options 
(the option must be marked as autowired) by looking up in the registry to find 
if there is a single instance of matching t [...]
   },
   "bodyTypes": {
-    "producerIn": { "carriers": [ "any" ], "byOption": [ { "option": 
"operation", "value": "updateUser", "carriers": [ "text", "pojo" ] }, { 
"option": "operation", "value": "updateGroup", "carriers": [ "text", "pojo" ] } 
], "description": "Not used by the list and get operations, which take the ids 
from the options or the CamelHwCloudIamUserId and CamelHwCloudIamGroupId 
exchange properties. updateUser needs an UpdateUserOption of the Huawei Cloud 
SDK or its JSON as text, and updateGroup  [...]
-    "producerOut": { "carriers": [ "text" ], "javaType": "java.lang.String", 
"format": "json", "description": "The JSON of the result as text: the array of 
the users or the groups for listUsers, listGroups and getGroupUsers, the user 
or the group for getUser, updateUser and updateGroup. The keys are the field 
names of the Huawei Cloud SDK, such as domainId" }
+    "producerIn": { "carriers": [ "any" ], "byOption": [ { "option": 
"operation", "value": "updateUser", "carriers": [ "text", "bytes", "stream", 
"file", "pojo" ] }, { "option": "operation", "value": "updateGroup", 
"carriers": [ "text", "bytes", "stream", "file", "pojo" ] } ], "description": 
"Not used by the list and get operations, which take the ids from the options 
or the CamelHwCloudIamUserId and CamelHwCloudIamGroupId exchange properties. 
updateUser needs an UpdateUserOption of the  [...]
+    "producerOut": { "carriers": [ "text" ], "javaType": "java.lang.String", 
"format": "json", "description": "The JSON of the result as text: the array of 
the users or the groups for listUsers, listGroups and getGroupUsers, the user 
or the group for getUser, updateUser and updateGroup. The keys are the field 
names of the IAM API, such as domain_id, as in the JSON of the request" }
   },
   "properties": {
     "operation": { "index": 0, "kind": "path", "displayName": "Operation", 
"group": "producer", "label": "producer", "required": true, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Operation to be performed" 
},
diff --git 
a/components/camel-huawei/camel-huaweicloud-iam/src/main/docs/hwcloud-iam-component.adoc
 
b/components/camel-huawei/camel-huaweicloud-iam/src/main/docs/hwcloud-iam-component.adoc
index 6a160eea888d..14361fd49625 100644
--- 
a/components/camel-huawei/camel-huaweicloud-iam/src/main/docs/hwcloud-iam-component.adoc
+++ 
b/components/camel-huawei/camel-huaweicloud-iam/src/main/docs/hwcloud-iam-component.adoc
@@ -71,14 +71,14 @@ If any of the above properties are set, they will override 
their corresponding q
 === Passing Options Through Exchange Body
 There are many options that can be submitted to 
https://support.huaweicloud.com/en-us/api-iam/iam_08_0011.html[update a user] 
(Table 4) or to 
https://support.huaweicloud.com/en-us/api-iam/iam_09_0004.html[update a group] 
(Table 4). Since there are multiple user/group options, they must be passed 
through the exchange body.
 
-For the `updateUser` operation, you can pass the user options as an 
UpdateUserOption object or a Json string:
+For the `updateUser` operation, you can pass the user options as an 
UpdateUserOption object or its JSON, as text, bytes, a stream or a file:
 
 ._Java-only: requires SDK UpdateUserOption builder_
 
 [source,java]
 ----
 from("direct:triggerRoute")
- .setBody(new 
UpdateUserOption().withName("user").withDescription("employee").withEmail("[email protected]"))
+ .setBody(constant(new 
UpdateUserOption().withName("user").withDescription("employee").withEmail("[email protected]")))
  
.to("hwcloud-iam:updateUser?userId=********&region=cn-north-4&accessKey=********&secretKey=********")
 ----
 
@@ -126,15 +126,15 @@ YAML::
 ----
 ====
 
-For the `updateGroup` operation, you can pass the group options as a 
KeystoneUpdateGroupOption object or a Json string:
+For the `updateGroup` operation, you can pass the group options as a 
KeystoneUpdateGroupOption object or its JSON, as text, bytes, a stream or a 
file:
 
 ._Java-only: requires SDK KeystoneUpdateGroupOption builder_
 
 [source,java]
 ----
 from("direct:triggerRoute")
- .setBody(new 
KeystoneUpdateGroupOption().withName("group").withDescription("employees").withDomainId("1234"))
- 
.to("hwcloud-iam:updateUser?groupId=********&region=cn-north-4&accessKey=********&secretKey=********")
+ .setBody(constant(new 
KeystoneUpdateGroupOption().withName("group").withDescription("employees").withDomainId("1234")))
+ 
.to("hwcloud-iam:updateGroup?groupId=********&region=cn-north-4&accessKey=********&secretKey=********")
 ----
 
 [tabs]
@@ -145,7 +145,7 @@ Java::
 ----
 from("direct:triggerRoute")
  
.setBody(constant("{\"name\":\"group\",\"description\":\"employees\",\"domain_id\":\"1234\"}"))
- 
.to("hwcloud-iam:updateUser?groupId=********&region=cn-north-4&accessKey=********&secretKey=********")
+ 
.to("hwcloud-iam:updateGroup?groupId=********&region=cn-north-4&accessKey=********&secretKey=********")
 ----
 
 XML::
@@ -157,7 +157,7 @@ XML::
   <setBody>
     
<constant>{"name":"group","description":"employees","domain_id":"1234"}</constant>
   </setBody>
-  <to 
uri="hwcloud-iam:updateUser?groupId=********&amp;region=cn-north-4&amp;accessKey=********&amp;secretKey=********"/>
+  <to 
uri="hwcloud-iam:updateGroup?groupId=********&amp;region=cn-north-4&amp;accessKey=********&amp;secretKey=********"/>
 </route>
 ----
 
@@ -172,7 +172,7 @@ YAML::
         - setBody:
             constant: 
'{"name":"group","description":"employees","domain_id":"1234"}'
         - to:
-            uri: hwcloud-iam:updateUser
+            uri: hwcloud-iam:updateGroup
             parameters:
               groupId: "********"
               region: cn-north-4
@@ -181,17 +181,28 @@ YAML::
 ----
 ====
 
+Another body fails the exchange with an `IllegalArgumentException`.
+
+=== JSON of the Results
+
+The operations set the exchange body to the JSON of their result, as text: the 
array of the users for `listUsers` and `getGroupUsers`, the array of the groups 
for `listGroups`, the user for `getUser` and `updateUser`, and the group for 
`updateGroup`. The keys are the field names of the IAM API, as in the JSON of 
the requests above, for example the result of `getUser`:
+
+[source,json]
+----
+{"domain_id":"1234","name":"user","email":"[email protected]","pwd_status":true}
+----
+
 === Using ServiceKey Configuration Bean
 
 Access key and secret keys are required to authenticate against cloud IAM 
service. You can avoid having them being exposed
-and scattered over in your endpoint uri by wrapping them inside a bean of 
class `org.apache.camel.component.huaweicloud.iam.models.ServiceKeys`.
+and scattered over in your endpoint uri by wrapping them inside a bean of 
class `org.apache.camel.component.huaweicloud.common.models.ServiceKeys`.
 Add it to the registry and let Camel look it up by referring the object via 
endpoint query parameter `serviceKeys`.
 
 Check the following code snippets:
 
 [source,xml]
 ----
-<bean id="myServiceKeyConfig" 
class="org.apache.camel.component.huaweicloud.iam.models.ServiceKeys">
+<bean id="myServiceKeyConfig" 
class="org.apache.camel.component.huaweicloud.common.models.ServiceKeys">
    <property name="accessKey" value="your_access_key" />
    <property name="secretKey" value="your_secret_key" />
 </bean>
diff --git 
a/components/camel-huawei/camel-huaweicloud-iam/src/main/java/org/apache/camel/component/huaweicloud/iam/IAMEndpoint.java
 
b/components/camel-huawei/camel-huaweicloud-iam/src/main/java/org/apache/camel/component/huaweicloud/iam/IAMEndpoint.java
index 40ce05b7fcd1..24f080b17a56 100644
--- 
a/components/camel-huawei/camel-huaweicloud-iam/src/main/java/org/apache/camel/component/huaweicloud/iam/IAMEndpoint.java
+++ 
b/components/camel-huawei/camel-huaweicloud-iam/src/main/java/org/apache/camel/component/huaweicloud/iam/IAMEndpoint.java
@@ -44,19 +44,23 @@ import org.apache.camel.util.ObjectHelper;
 @BodyTypes(producerIn = @BodyType(value = Carrier.ANY,
                                   byOption = {
                                           @BodyType.When(option = "operation", 
value = "updateUser",
-                                                         carriers = { 
Carrier.TEXT, Carrier.POJO }),
+                                                         carriers = {
+                                                                 Carrier.TEXT, 
Carrier.BYTES, Carrier.STREAM, Carrier.FILE,
+                                                                 Carrier.POJO 
}),
                                           @BodyType.When(option = "operation", 
value = "updateGroup",
-                                                         carriers = { 
Carrier.TEXT, Carrier.POJO }) },
+                                                         carriers = {
+                                                                 Carrier.TEXT, 
Carrier.BYTES, Carrier.STREAM, Carrier.FILE,
+                                                                 Carrier.POJO 
}) },
                                   description = "Not used by the list and get 
operations, which take the ids from the options"
                                                 + " or the 
CamelHwCloudIamUserId and CamelHwCloudIamGroupId exchange properties."
-                                                + " updateUser needs an 
UpdateUserOption of the Huawei Cloud SDK or its JSON as"
-                                                + " text, and updateGroup a 
KeystoneUpdateGroupOption or its JSON: another body,"
-                                                + " JSON as bytes or a stream 
too, fails"),
+                                                + " updateUser needs an 
UpdateUserOption of the Huawei Cloud SDK or its JSON, and"
+                                                + " updateGroup a 
KeystoneUpdateGroupOption or its JSON, as text, bytes, a stream"
+                                                + " or a file: another body 
fails"),
            producerOut = @BodyType(value = Carrier.TEXT, javaType = 
"java.lang.String", format = "json",
                                    description = "The JSON of the result as 
text: the array of the users or the groups for"
                                                  + " listUsers, listGroups and 
getGroupUsers, the user or the group for getUser,"
-                                                 + " updateUser and 
updateGroup. The keys are the field names of the Huawei Cloud"
-                                                 + " SDK, such as domainId"))
+                                                 + " updateUser and 
updateGroup. The keys are the field names of the IAM API, such"
+                                                 + " as domain_id, as in the 
JSON of the request"))
 public class IAMEndpoint extends DefaultEndpoint {
 
     @UriPath(description = "Operation to be performed", displayName = 
"Operation", label = "producer")
diff --git 
a/components/camel-huawei/camel-huaweicloud-iam/src/main/java/org/apache/camel/component/huaweicloud/iam/IAMProducer.java
 
b/components/camel-huawei/camel-huaweicloud-iam/src/main/java/org/apache/camel/component/huaweicloud/iam/IAMProducer.java
index 4500dd105f4e..ce84daed6785 100644
--- 
a/components/camel-huawei/camel-huaweicloud-iam/src/main/java/org/apache/camel/component/huaweicloud/iam/IAMProducer.java
+++ 
b/components/camel-huawei/camel-huaweicloud-iam/src/main/java/org/apache/camel/component/huaweicloud/iam/IAMProducer.java
@@ -16,9 +16,11 @@
  */
 package org.apache.camel.component.huaweicloud.iam;
 
+import com.fasterxml.jackson.core.JsonLocation;
 import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.gson.Gson;
+import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
 import com.huaweicloud.sdk.iam.v3.IamClient;
 import com.huaweicloud.sdk.iam.v3.model.KeystoneListGroupsRequest;
 import com.huaweicloud.sdk.iam.v3.model.KeystoneListGroupsResponse;
@@ -37,6 +39,7 @@ import com.huaweicloud.sdk.iam.v3.model.UpdateUserRequest;
 import com.huaweicloud.sdk.iam.v3.model.UpdateUserRequestBody;
 import com.huaweicloud.sdk.iam.v3.model.UpdateUserResponse;
 import org.apache.camel.Exchange;
+import org.apache.camel.InvalidPayloadException;
 import org.apache.camel.component.huaweicloud.iam.constants.IAMOperations;
 import org.apache.camel.component.huaweicloud.iam.constants.IAMProperties;
 import org.apache.camel.component.huaweicloud.iam.models.ClientConfigurations;
@@ -49,7 +52,7 @@ public class IAMProducer extends DefaultProducer {
     private static final Logger LOG = 
LoggerFactory.getLogger(IAMProducer.class);
     private IAMEndpoint endpoint;
     private IamClient iamClient;
-    private Gson gson;
+    private ObjectMapper mapper;
 
     public IAMProducer(IAMEndpoint endpoint) {
         super(endpoint);
@@ -59,7 +62,8 @@ public class IAMProducer extends DefaultProducer {
     @Override
     protected void doStart() throws Exception {
         super.doStart();
-        this.gson = new Gson();
+        // the Jackson annotations of the SDK model give the field names of 
the IAM API, such as domain_id
+        this.mapper = new ObjectMapper();
     }
 
     public void process(Exchange exchange) throws Exception {
@@ -102,22 +106,24 @@ public class IAMProducer extends DefaultProducer {
     /**
      * Perform list users operation
      *
-     * @param exchange
+     * @param  exchange
+     * @throws JsonProcessingException
      */
-    private void listUsers(Exchange exchange) {
+    private void listUsers(Exchange exchange) throws JsonProcessingException {
         // invoke list users method and map return object to exchange body
         KeystoneListUsersRequest request = new KeystoneListUsersRequest();
         KeystoneListUsersResponse response = 
iamClient.keystoneListUsers(request);
-        exchange.getMessage().setBody(gson.toJson(response.getUsers()));
+        
exchange.getMessage().setBody(mapper.writeValueAsString(response.getUsers()));
     }
 
     /**
      * Perform get user operation
      *
-     * @param exchange
-     * @param clientConfigurations
+     * @param  exchange
+     * @param  clientConfigurations
+     * @throws JsonProcessingException
      */
-    private void getUser(Exchange exchange, ClientConfigurations 
clientConfigurations) {
+    private void getUser(Exchange exchange, ClientConfigurations 
clientConfigurations) throws JsonProcessingException {
         // check for user id, which is mandatory to get user
         if (ObjectHelper.isEmpty(clientConfigurations.getUserId())) {
             if (LOG.isErrorEnabled()) {
@@ -130,29 +136,35 @@ public class IAMProducer extends DefaultProducer {
         ShowUserRequest request = new ShowUserRequest()
                 .withUserId(clientConfigurations.getUserId());
         ShowUserResponse response = iamClient.showUser(request);
-        exchange.getMessage().setBody(gson.toJson(response.getUser()));
+        
exchange.getMessage().setBody(mapper.writeValueAsString(response.getUser()));
     }
 
     /**
      * Perform update user operation
      *
-     * @param exchange
-     * @param clientConfigurations
+     * @param  exchange
+     * @param  clientConfigurations
+     * @throws JsonProcessingException
+     * @throws InvalidPayloadException if the body cannot be read as text
      */
-    private void updateUser(Exchange exchange, ClientConfigurations 
clientConfigurations) {
-        // checking for valid exchange body containing user information. Body 
must be an UpdateUserOption object or a JSON string
+    private void updateUser(Exchange exchange, ClientConfigurations 
clientConfigurations)
+            throws JsonProcessingException, InvalidPayloadException {
+        // checking for valid exchange body containing user information. Body 
must be an UpdateUserOption object or its
+        // JSON, as text, bytes, a stream or a file
         Object body = exchange.getMessage().getBody();
-        UpdateUserOption userOption;
+        UpdateUserOption userOption = null;
         if (body instanceof UpdateUserOption) {
             userOption = (UpdateUserOption) body;
-        } else if (body instanceof String) {
-            String strBody = (String) body;
+        } else if (body != null) {
+            String strBody = 
exchange.getMessage().getMandatoryBody(String.class);
             try {
-                userOption = new ObjectMapper().readValue(strBody, 
UpdateUserOption.class);
+                userOption = mapper.readValue(strBody, UpdateUserOption.class);
             } catch (JsonProcessingException e) {
-                throw new IllegalArgumentException("String request body must 
be a valid JSON");
+                throw new IllegalArgumentException(
+                        "Request body must be a valid JSON representation of 
an UpdateUserOption" + jsonErrorDetail(e));
             }
-        } else {
+        }
+        if (userOption == null) {
             throw new IllegalArgumentException(
                     "Exchange body is mandatory and should be a valid JSON 
string or UpdateUserOption object");
         }
@@ -172,28 +184,30 @@ public class IAMProducer extends DefaultProducer {
                 .withBody(requestBody)
                 .withUserId(clientConfigurations.getUserId());
         UpdateUserResponse response = iamClient.updateUser(request);
-        exchange.getMessage().setBody(gson.toJson(response.getUser()));
+        
exchange.getMessage().setBody(mapper.writeValueAsString(response.getUser()));
     }
 
     /**
      * Perform list groups operation
      *
-     * @param exchange
+     * @param  exchange
+     * @throws JsonProcessingException
      */
-    private void listGroups(Exchange exchange) {
+    private void listGroups(Exchange exchange) throws JsonProcessingException {
         // invoke list groups method and map return object to exchange body
         KeystoneListGroupsRequest request = new KeystoneListGroupsRequest();
         KeystoneListGroupsResponse response = 
iamClient.keystoneListGroups(request);
-        exchange.getMessage().setBody(gson.toJson(response.getGroups()));
+        
exchange.getMessage().setBody(mapper.writeValueAsString(response.getGroups()));
     }
 
     /**
      * Perform get group users operation
      *
-     * @param exchange
-     * @param clientConfigurations
+     * @param  exchange
+     * @param  clientConfigurations
+     * @throws JsonProcessingException
      */
-    private void getGroupUsers(Exchange exchange, ClientConfigurations 
clientConfigurations) {
+    private void getGroupUsers(Exchange exchange, ClientConfigurations 
clientConfigurations) throws JsonProcessingException {
         // check for group id, which is mandatory for getting group users
         if (ObjectHelper.isEmpty(clientConfigurations.getGroupId())) {
             if (LOG.isErrorEnabled()) {
@@ -206,29 +220,36 @@ public class IAMProducer extends DefaultProducer {
         KeystoneListUsersForGroupByAdminRequest request = new 
KeystoneListUsersForGroupByAdminRequest()
                 .withGroupId(clientConfigurations.getGroupId());
         KeystoneListUsersForGroupByAdminResponse response = 
iamClient.keystoneListUsersForGroupByAdmin(request);
-        exchange.getMessage().setBody(gson.toJson(response.getUsers()));
+        
exchange.getMessage().setBody(mapper.writeValueAsString(response.getUsers()));
     }
 
     /**
      * Perform update group operation
      *
-     * @param exchange
-     * @param clientConfigurations
+     * @param  exchange
+     * @param  clientConfigurations
+     * @throws JsonProcessingException
+     * @throws InvalidPayloadException if the body cannot be read as text
      */
-    private void updateGroup(Exchange exchange, ClientConfigurations 
clientConfigurations) {
-        // checking for valid exchange body containing group information. Body 
must be an KeystoneUpdateGroupOption object or a JSON string
+    private void updateGroup(Exchange exchange, ClientConfigurations 
clientConfigurations)
+            throws JsonProcessingException, InvalidPayloadException {
+        // checking for valid exchange body containing group information. Body 
must be a KeystoneUpdateGroupOption object
+        // or its JSON, as text, bytes, a stream or a file
         Object body = exchange.getMessage().getBody();
-        KeystoneUpdateGroupOption groupOption;
+        KeystoneUpdateGroupOption groupOption = null;
         if (body instanceof KeystoneUpdateGroupOption) {
             groupOption = (KeystoneUpdateGroupOption) body;
-        } else if (body instanceof String) {
-            String strBody = (String) body;
+        } else if (body != null) {
+            String strBody = 
exchange.getMessage().getMandatoryBody(String.class);
             try {
-                groupOption = new ObjectMapper().readValue(strBody, 
KeystoneUpdateGroupOption.class);
+                groupOption = mapper.readValue(strBody, 
KeystoneUpdateGroupOption.class);
             } catch (JsonProcessingException e) {
-                throw new IllegalArgumentException("String request body must 
be a valid JSON with the proper keys");
+                throw new IllegalArgumentException(
+                        "Request body must be a valid JSON representation of a 
KeystoneUpdateGroupOption"
+                                                   + jsonErrorDetail(e));
             }
-        } else {
+        }
+        if (groupOption == null) {
             throw new IllegalArgumentException(
                     "Exchange body is mandatory and should be a valid JSON 
string or KeystoneUpdateGroupOption object");
         }
@@ -248,7 +269,49 @@ public class IAMProducer extends DefaultProducer {
                 .withBody(requestBody)
                 .withGroupId(clientConfigurations.getGroupId());
         KeystoneUpdateGroupResponse response = 
iamClient.keystoneUpdateGroup(request);
-        exchange.getMessage().setBody(gson.toJson(response.getGroup()));
+        
exchange.getMessage().setBody(mapper.writeValueAsString(response.getGroup()));
+    }
+
+    /**
+     * Where the JSON of a request body is wrong, such as (unknown field 
domainId at line 1, column 14) or (wrong value
+     * for field enabled at line 1, column 30). It names the fields only: the 
message of Jackson can quote a value of
+     * the body, such as a password.
+     */
+    private static String jsonErrorDetail(JsonProcessingException e) {
+        String path = e instanceof JsonMappingException jme ? fieldPath(jme) : 
"";
+        String detail;
+        if (e instanceof UnrecognizedPropertyException) {
+            detail = "unknown field " + path;
+        } else if (!path.isEmpty()) {
+            detail = "wrong value for field " + path;
+        } else {
+            detail = "error";
+        }
+        JsonLocation location = e.getLocation();
+        if (location != null && location.getLineNr() > 0) {
+            detail += " at line " + location.getLineNr() + ", column " + 
location.getColumnNr();
+        } else if (path.isEmpty()) {
+            return "";
+        }
+        return " (" + detail + ")";
+    }
+
+    /**
+     * The field names and the array indexes of the path of a mapping error.
+     */
+    private static String fieldPath(JsonMappingException e) {
+        StringBuilder path = new StringBuilder();
+        for (JsonMappingException.Reference reference : e.getPath()) {
+            if (reference.getFieldName() != null) {
+                if (!path.isEmpty()) {
+                    path.append('.');
+                }
+                path.append(reference.getFieldName());
+            } else if (reference.getIndex() >= 0) {
+                path.append('[').append(reference.getIndex()).append(']');
+            }
+        }
+        return path.toString();
     }
 
     /**
diff --git 
a/components/camel-huawei/camel-huaweicloud-iam/src/test/java/org/apache/camel/component/huaweicloud/iam/GetUserTest.java
 
b/components/camel-huawei/camel-huaweicloud-iam/src/test/java/org/apache/camel/component/huaweicloud/iam/GetUserTest.java
index f04bc1413459..ed3ce58a7c3b 100644
--- 
a/components/camel-huawei/camel-huaweicloud-iam/src/test/java/org/apache/camel/component/huaweicloud/iam/GetUserTest.java
+++ 
b/components/camel-huawei/camel-huaweicloud-iam/src/test/java/org/apache/camel/component/huaweicloud/iam/GetUserTest.java
@@ -68,7 +68,7 @@ public class GetUserTest extends CamelTestSupport {
 
         mock.assertIsSatisfied();
 
-        assertEquals("{\"domainId\":\"138\",\"name\":\"User 
15\",\"email\":\"[email protected]\"}",
+        assertEquals("{\"domain_id\":\"138\",\"name\":\"User 
15\",\"email\":\"[email protected]\"}",
                 responseExchange.getIn().getBody(String.class));
     }
 }
diff --git 
a/components/camel-huawei/camel-huaweicloud-iam/src/test/java/org/apache/camel/component/huaweicloud/iam/IAMProducerJsonTest.java
 
b/components/camel-huawei/camel-huaweicloud-iam/src/test/java/org/apache/camel/component/huaweicloud/iam/IAMProducerJsonTest.java
new file mode 100644
index 000000000000..291f46c4d2d4
--- /dev/null
+++ 
b/components/camel-huawei/camel-huaweicloud-iam/src/test/java/org/apache/camel/component/huaweicloud/iam/IAMProducerJsonTest.java
@@ -0,0 +1,371 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.huaweicloud.iam;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.huaweicloud.sdk.iam.v3.model.KeystoneGroupResult;
+import com.huaweicloud.sdk.iam.v3.model.KeystoneListGroupsRequest;
+import com.huaweicloud.sdk.iam.v3.model.KeystoneListGroupsResponse;
+import 
com.huaweicloud.sdk.iam.v3.model.KeystoneListUsersForGroupByAdminRequest;
+import 
com.huaweicloud.sdk.iam.v3.model.KeystoneListUsersForGroupByAdminResponse;
+import com.huaweicloud.sdk.iam.v3.model.KeystoneListUsersRequest;
+import com.huaweicloud.sdk.iam.v3.model.KeystoneListUsersResponse;
+import com.huaweicloud.sdk.iam.v3.model.KeystoneListUsersResult;
+import com.huaweicloud.sdk.iam.v3.model.KeystoneUpdateGroupOption;
+import com.huaweicloud.sdk.iam.v3.model.KeystoneUpdateGroupRequest;
+import com.huaweicloud.sdk.iam.v3.model.KeystoneUpdateGroupResponse;
+import com.huaweicloud.sdk.iam.v3.model.KeystoneUserResult;
+import com.huaweicloud.sdk.iam.v3.model.ShowUserRequest;
+import com.huaweicloud.sdk.iam.v3.model.ShowUserResponse;
+import com.huaweicloud.sdk.iam.v3.model.ShowUserResult;
+import com.huaweicloud.sdk.iam.v3.model.UpdateUserOption;
+import com.huaweicloud.sdk.iam.v3.model.UpdateUserRequest;
+import com.huaweicloud.sdk.iam.v3.model.UpdateUserResponse;
+import com.huaweicloud.sdk.iam.v3.model.UpdateUserResult;
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * The request of updateUser and updateGroup as JSON text, bytes, a stream or 
a file, and the field names of the JSON
+ * results.
+ */
+class IAMProducerJsonTest extends CamelTestSupport {
+
+    private static final String USER_JSON = "{\"name\":\"User 
19\",\"description\":\"Zürich ✓\",\"pwd_status\":true}";
+    private static final String GROUP_JSON = "{\"name\":\"Group 
43\",\"description\":\"Zürich ✓\",\"domain_id\":\"123\"}";
+
+    private final ObjectMapper mapper = new ObjectMapper();
+
+    TestConfiguration testConfiguration = new TestConfiguration();
+
+    @BindToRegistry("iamClient")
+    CapturingClient client = new CapturingClient();
+
+    @TempDir
+    Path tempDir;
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        String options = "accessKey=" + 
testConfiguration.getProperty("accessKey") +
+                         "&secretKey=" + 
testConfiguration.getProperty("secretKey") +
+                         "&region=" + testConfiguration.getProperty("region") +
+                         "&ignoreSslVerification=true" +
+                         "&iamClient=#iamClient";
+        return new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:updateUser")
+                        .to("hwcloud-iam:updateUser?userId=" + 
testConfiguration.getProperty("userId") + "&" + options);
+                from("direct:updateGroup")
+                        .to("hwcloud-iam:updateGroup?groupId=" + 
testConfiguration.getProperty("groupId") + "&" + options);
+                from("direct:getUser")
+                        .to("hwcloud-iam:getUser?userId=" + 
testConfiguration.getProperty("userId") + "&" + options);
+                from("direct:listUsers")
+                        .to("hwcloud-iam:listUsers?" + options);
+                from("direct:listGroups")
+                        .to("hwcloud-iam:listGroups?" + options);
+                from("direct:getGroupUsers")
+                        .to("hwcloud-iam:getGroupUsers?groupId=" + 
testConfiguration.getProperty("groupId") + "&" + options);
+            }
+        };
+    }
+
+    @Test
+    void updateUserReadsJsonBytes() {
+        
assertUserOption(updateUser(USER_JSON.getBytes(StandardCharsets.UTF_8)));
+    }
+
+    @Test
+    void updateUserReadsJsonStream() {
+        assertUserOption(updateUser(new 
ByteArrayInputStream(USER_JSON.getBytes(StandardCharsets.UTF_8))));
+    }
+
+    @Test
+    void updateUserReadsJsonFile() throws Exception {
+        File file = Files.writeString(tempDir.resolve("user.json"), USER_JSON, 
StandardCharsets.UTF_8).toFile();
+
+        assertUserOption(updateUser(file));
+    }
+
+    @Test
+    void updateUserFailsOnText() {
+        Exchange exchange = send("direct:updateUser", "{\"name\":\"User 
19\",\"password\":secret-value}");
+
+        Exception failure = assertInstanceOf(IllegalArgumentException.class, 
exchange.getException());
+        assertFalse(failure.getMessage().contains("secret"), 
failure.getMessage());
+        assertNull(client.userRequest);
+    }
+
+    @Test
+    void updateUserFailureNamesTheFieldWithAWrongValue() {
+        Exchange exchange = send("direct:updateUser", "{\"name\":\"User 
19\",\"enabled\":\"secret-value\"}");
+
+        Exception failure = assertInstanceOf(IllegalArgumentException.class, 
exchange.getException());
+        assertTrue(failure.getMessage().contains("(wrong value for field 
enabled at line 1, column "), failure.getMessage());
+        assertFalse(failure.getMessage().contains("secret"), 
failure.getMessage());
+        assertNull(client.userRequest);
+    }
+
+    @Test
+    void updateUserFailsOnBytesThatAreNotJson() {
+        Exchange exchange = send("direct:updateUser", new byte[] { (byte) 
0xff, (byte) 0xfe, 'x' });
+
+        assertInstanceOf(IllegalArgumentException.class, 
exchange.getException());
+        assertNull(client.userRequest);
+    }
+
+    @Test
+    void updateUserFailsWithoutBody() {
+        Exchange exchange = send("direct:updateUser", null);
+
+        assertInstanceOf(IllegalArgumentException.class, 
exchange.getException());
+        assertNull(client.userRequest);
+    }
+
+    @Test
+    void updateGroupReadsJsonBytes() {
+        
assertGroupOption(updateGroup(GROUP_JSON.getBytes(StandardCharsets.UTF_8)));
+    }
+
+    @Test
+    void updateGroupReadsJsonStream() {
+        assertGroupOption(updateGroup(new 
ByteArrayInputStream(GROUP_JSON.getBytes(StandardCharsets.UTF_8))));
+    }
+
+    @Test
+    void updateGroupReadsJsonFile() throws Exception {
+        File file = Files.writeString(tempDir.resolve("group.json"), 
GROUP_JSON, StandardCharsets.UTF_8).toFile();
+
+        assertGroupOption(updateGroup(file));
+    }
+
+    @Test
+    void updateGroupFailsOnUnknownField() {
+        Exchange exchange = send("direct:updateGroup", "{\"name\":\"Group 
43\",\"domainId\":\"123\"}");
+
+        Exception failure = assertInstanceOf(IllegalArgumentException.class, 
exchange.getException());
+        assertTrue(failure.getMessage().contains("(unknown field domainId at 
line 1, column "), failure.getMessage());
+        assertNull(client.groupRequest);
+    }
+
+    @Test
+    void updateGroupFailureNamesTheFieldWithAWrongValue() {
+        Exchange exchange = send("direct:updateGroup", 
"{\"name\":{\"first\":\"secret-value\"}}");
+
+        Exception failure = assertInstanceOf(IllegalArgumentException.class, 
exchange.getException());
+        assertTrue(failure.getMessage().contains("(wrong value for field name 
at line 1, column "), failure.getMessage());
+        assertFalse(failure.getMessage().contains("secret"), 
failure.getMessage());
+        assertNull(client.groupRequest);
+    }
+
+    @Test
+    void updateGroupFailsWithoutBody() {
+        Exchange exchange = send("direct:updateGroup", null);
+
+        assertInstanceOf(IllegalArgumentException.class, 
exchange.getException());
+        assertNull(client.groupRequest);
+    }
+
+    @Test
+    void getUserGivesApiFieldNames() throws Exception {
+        Map<String, Object> user = mapper.readValue(result("direct:getUser"), 
new TypeReference<>() {
+        });
+
+        assertEquals(Map.of("domain_id", "d1", "name", "User 15", 
"pwd_status", true, "is_domain_owner", false,
+                "last_login_time", "2026-01-01T10:00:00Z", "xuser_id", "x1"), 
user);
+    }
+
+    @Test
+    void listUsersGivesApiFieldNames() throws Exception {
+        List<Map<String, Object>> users = 
mapper.readValue(result("direct:listUsers"), new TypeReference<>() {
+        });
+
+        assertEquals(List.of(Map.of("domain_id", "d1", "name", "User 1", 
"last_project_id", "p1",
+                "password_expires_at", "2027-01-01T10:00:00Z", "pwd_strength", 
"high")), users);
+    }
+
+    @Test
+    void listGroupsGivesApiFieldNames() throws Exception {
+        List<Map<String, Object>> groups = 
mapper.readValue(result("direct:listGroups"), new TypeReference<>() {
+        });
+
+        assertEquals(List.of(Map.of("domain_id", "d1", "name", "Group 1", 
"create_time", 1767261600000L)), groups);
+    }
+
+    @Test
+    void getGroupUsersGivesApiFieldNames() throws Exception {
+        List<Map<String, Object>> users = 
mapper.readValue(result("direct:getGroupUsers"), new TypeReference<>() {
+        });
+
+        assertEquals(List.of(Map.of("domain_id", "d1", "name", "User 9", 
"last_project_id", "p1", "password_expires_at",
+                "2027-01-01T10:00:00Z", "pwd_status", true)), users);
+    }
+
+    @Test
+    void updateUserGivesApiFieldNames() throws Exception {
+        Exchange exchange = send("direct:updateUser", USER_JSON);
+
+        assertNull(exchange.getException(), () -> "updateUser failed: " + 
exchange.getException());
+        Map<String, Object> user = 
mapper.readValue(exchange.getMessage().getBody(String.class), new 
TypeReference<>() {
+        });
+        assertEquals(Map.of("name", "User 19", "xuser_id", "x1", "pwd_status", 
true, "is_domain_owner", false,
+                "password_expires_at", "2027-01-01T10:00:00Z"), user);
+    }
+
+    @Test
+    void updateGroupGivesApiFieldNames() {
+        Exchange exchange = send("direct:updateGroup", GROUP_JSON);
+
+        assertNull(exchange.getException(), () -> "updateGroup failed: " + 
exchange.getException());
+        assertEquals("{\"description\":\"Group 
description\",\"domain_id\":\"123\",\"name\":\"Group 43\"}",
+                exchange.getMessage().getBody(String.class));
+    }
+
+    private static void assertUserOption(UpdateUserOption option) {
+        assertEquals("User 19", option.getName());
+        assertEquals("Zürich ✓", option.getDescription());
+        assertEquals(Boolean.TRUE, option.getPwdStatus());
+    }
+
+    private static void assertGroupOption(KeystoneUpdateGroupOption option) {
+        assertEquals("Group 43", option.getName());
+        assertEquals("Zürich ✓", option.getDescription());
+        assertEquals("123", option.getDomainId());
+    }
+
+    private Exchange send(String uri, Object body) {
+        return template.send(uri, e -> e.getMessage().setBody(body));
+    }
+
+    private String result(String uri) {
+        Exchange exchange = send(uri, null);
+        assertNull(exchange.getException(), () -> uri + " failed: " + 
exchange.getException());
+        return exchange.getMessage().getBody(String.class);
+    }
+
+    private UpdateUserOption updateUser(Object body) {
+        Exchange exchange = send("direct:updateUser", body);
+
+        assertNull(exchange.getException(), () -> "updateUser failed: " + 
exchange.getException());
+        assertNotNull(client.userRequest);
+        assertEquals(testConfiguration.getProperty("userId"), 
client.userRequest.getUserId());
+        return client.userRequest.getBody().getUser();
+    }
+
+    private KeystoneUpdateGroupOption updateGroup(Object body) {
+        Exchange exchange = send("direct:updateGroup", body);
+
+        assertNull(exchange.getException(), () -> "updateGroup failed: " + 
exchange.getException());
+        assertNotNull(client.groupRequest);
+        assertEquals(testConfiguration.getProperty("groupId"), 
client.groupRequest.getGroupId());
+        return client.groupRequest.getBody().getGroup();
+    }
+
+    /**
+     * Keeps the update requests, and gives results with fields whose API name 
is not the Java name.
+     */
+    static class CapturingClient extends IAMMockClient {
+        UpdateUserRequest userRequest;
+        KeystoneUpdateGroupRequest groupRequest;
+
+        CapturingClient() {
+            super(null);
+        }
+
+        @Override
+        public UpdateUserResponse updateUser(UpdateUserRequest request) {
+            this.userRequest = request;
+            UpdateUserResult user = new UpdateUserResult()
+                    .withName("User 19")
+                    .withXuserId("x1")
+                    .withPwdStatus(true)
+                    .withIsDomainOwner(false)
+                    .withPasswordExpiresAt("2027-01-01T10:00:00Z");
+            return new UpdateUserResponse().withUser(user);
+        }
+
+        @Override
+        public KeystoneUpdateGroupResponse 
keystoneUpdateGroup(KeystoneUpdateGroupRequest request) {
+            this.groupRequest = request;
+            return super.keystoneUpdateGroup(request);
+        }
+
+        @Override
+        public ShowUserResponse showUser(ShowUserRequest request) {
+            ShowUserResult user = new ShowUserResult()
+                    .withDomainId("d1")
+                    .withName("User 15")
+                    .withPwdStatus(true)
+                    .withIsDomainOwner(false)
+                    .withLastLoginTime("2026-01-01T10:00:00Z")
+                    .withXuserId("x1");
+            return new ShowUserResponse().withUser(user);
+        }
+
+        @Override
+        public KeystoneListUsersResponse 
keystoneListUsers(KeystoneListUsersRequest request) {
+            KeystoneListUsersResult user = new KeystoneListUsersResult()
+                    .withDomainId("d1")
+                    .withName("User 1")
+                    .withLastProjectId("p1")
+                    .withPasswordExpiresAt("2027-01-01T10:00:00Z")
+                    .withPwdStrength("high");
+            return new KeystoneListUsersResponse().withUsers(List.of(user));
+        }
+
+        @Override
+        public KeystoneListUsersForGroupByAdminResponse 
keystoneListUsersForGroupByAdmin(
+                KeystoneListUsersForGroupByAdminRequest request) {
+            KeystoneUserResult user = new KeystoneUserResult()
+                    .withDomainId("d1")
+                    .withName("User 9")
+                    .withLastProjectId("p1")
+                    .withPasswordExpiresAt("2027-01-01T10:00:00Z")
+                    .withPwdStatus(true);
+            return new 
KeystoneListUsersForGroupByAdminResponse().withUsers(List.of(user));
+        }
+
+        @Override
+        public KeystoneListGroupsResponse 
keystoneListGroups(KeystoneListGroupsRequest request) {
+            KeystoneGroupResult group = new KeystoneGroupResult()
+                    .withDomainId("d1")
+                    .withName("Group 1")
+                    .withCreateTime(1767261600000L);
+            return new KeystoneListGroupsResponse().withGroups(List.of(group));
+        }
+    }
+}
diff --git 
a/components/camel-huawei/camel-huaweicloud-iam/src/test/java/org/apache/camel/component/huaweicloud/iam/ListUsersTest.java
 
b/components/camel-huawei/camel-huaweicloud-iam/src/test/java/org/apache/camel/component/huaweicloud/iam/ListUsersTest.java
index 56e26eb7bbdb..bfcf40f203e4 100644
--- 
a/components/camel-huawei/camel-huaweicloud-iam/src/test/java/org/apache/camel/component/huaweicloud/iam/ListUsersTest.java
+++ 
b/components/camel-huawei/camel-huaweicloud-iam/src/test/java/org/apache/camel/component/huaweicloud/iam/ListUsersTest.java
@@ -61,7 +61,7 @@ public class ListUsersTest extends CamelTestSupport {
 
         mock.assertIsSatisfied();
 
-        assertEquals("[{\"domainId\":\"123\",\"name\":\"User 
1\"},{\"domainId\":\"456\",\"name\":\"User 2\"}]",
+        assertEquals("[{\"domain_id\":\"123\",\"name\":\"User 
1\"},{\"domain_id\":\"456\",\"name\":\"User 2\"}]",
                 responseExchange.getIn().getBody(String.class));
     }
 }
diff --git 
a/components/camel-huawei/camel-huaweicloud-iam/src/test/java/org/apache/camel/component/huaweicloud/iam/UpdateGroupJsonTest.java
 
b/components/camel-huawei/camel-huaweicloud-iam/src/test/java/org/apache/camel/component/huaweicloud/iam/UpdateGroupJsonTest.java
index 4342e783db21..2f5bd561975c 100644
--- 
a/components/camel-huawei/camel-huaweicloud-iam/src/test/java/org/apache/camel/component/huaweicloud/iam/UpdateGroupJsonTest.java
+++ 
b/components/camel-huawei/camel-huaweicloud-iam/src/test/java/org/apache/camel/component/huaweicloud/iam/UpdateGroupJsonTest.java
@@ -67,7 +67,7 @@ public class UpdateGroupJsonTest extends CamelTestSupport {
 
         mock.assertIsSatisfied();
 
-        assertEquals("{\"description\":\"Group 
description\",\"domainId\":\"123\",\"name\":\"Group 43\"}",
+        assertEquals("{\"description\":\"Group 
description\",\"domain_id\":\"123\",\"name\":\"Group 43\"}",
                 responseExchange.getIn().getBody(String.class));
     }
 }
diff --git 
a/components/camel-huawei/camel-huaweicloud-iam/src/test/java/org/apache/camel/component/huaweicloud/iam/UpdateGroupTest.java
 
b/components/camel-huawei/camel-huaweicloud-iam/src/test/java/org/apache/camel/component/huaweicloud/iam/UpdateGroupTest.java
index 1735a21b8da4..e402634be8db 100644
--- 
a/components/camel-huawei/camel-huaweicloud-iam/src/test/java/org/apache/camel/component/huaweicloud/iam/UpdateGroupTest.java
+++ 
b/components/camel-huawei/camel-huaweicloud-iam/src/test/java/org/apache/camel/component/huaweicloud/iam/UpdateGroupTest.java
@@ -72,7 +72,7 @@ public class UpdateGroupTest extends CamelTestSupport {
 
         mock.assertIsSatisfied();
 
-        assertEquals("{\"description\":\"Group 
description\",\"domainId\":\"123\",\"name\":\"Group 43\"}",
+        assertEquals("{\"description\":\"Group 
description\",\"domain_id\":\"123\",\"name\":\"Group 43\"}",
                 responseExchange.getIn().getBody(String.class));
     }
 }
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index d5345c3d94e0..cefd84783081 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -3198,6 +3198,18 @@ consumer stops after the message that is being routed. 
The other messages of tha
 was resumed. They are now kept and routed by the first poll after the resume, 
together with the messages posted in
 the meantime. They are kept in memory only: a route that is stopped and 
started again does not route them.
 
+=== camel-huaweicloud-iam - the JSON results use the field names of the IAM API
+
+The operations of `hwcloud-iam` now write their results (the users or the 
groups of `listUsers`, `listGroups` and
+`getGroupUsers`, and the user or the group of `getUser`, `updateUser` and 
`updateGroup`) as JSON with the field names of
+the IAM API. These are the names that the JSON requests of `updateUser` and 
`updateGroup` already used, and that
+`hwcloud-dms` writes too. Before, the results used the Java field names of the 
Huawei Cloud SDK. For example,
+`domainId` is now `domain_id`, `pwdStatus` is `pwd_status`, `lastProjectId` is 
`last_project_id`, `passwordExpiresAt`
+is `password_expires_at`, `createTime` is `create_time` and `isDomainOwner` is 
`is_domain_owner`. A route that reads
+these fields, for example with the JSONPath `$.domainId`, must use the new 
names.
+
+The component no longer depends on Gson.
+
 === camel-mail - mails of a poll that were not routed, with an 
idempotentRepository
 
 With an `idempotentRepository`, the mail consumer now removes the keys of the 
mails that a poll retrieved but did not

Reply via email to