My web site send a JWT (built in php) to a my app developped in Java.
The JWT contains a JSON String inside a custom field called DATI.
I use the library JJWT in order to decript the string contained inside DATI field:
Claims MY_CLAIMS = Jwts.parser().setSigningKey(SECRET_KEY__Byte).parseClaimsJws(STRING_JWT).getBody();
ArrayList ARRAY = MY_CLAIMS .get("DATI", ArrayList.class);
String DECODED_STRING_INSIDE_DATI =String.valueOf(ARRAY);
I get the string "DECODED_STRING_INSIDE_DATI" (that is a JSON String) in the correct way, but for some reason the quotation marks (") are removed:
[{id=3, id_rivenditore=-1, id_cliente=-1, ip_address=192.168.1.6, nome=DonalDuck, note=ByBye, enabled=1}]
I tested STRING_JWT in "https://jwt.io/", and there I get correctly the quotation marks:
{
"iss": "www.mySite.it",
"exp": 1536913435,
"sub": "WebApp",
"DATI": [
{
"id": "3",
"id_rivenditore": "-1",
"id_cliente": "-1",
"ip_address": "192.168.1.6",
"nome": "DonalDuck",
"note": "ByBye",
"enabled": "1"
}
]
}
I really don't know how to solve it, because I cannot read the JSON string in the right way. I use jackson library to read Json String
This might help,
You already have ArrayList containing required claims as,
ArrayList ARRAY = MY_CLAIMS.get("DATI", ArrayList.class);
To get a JSON string of claims included in this ArrayList, try below code.
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(out, ARRAY);
byte[] data = out.toByteArray();
String str = new String(data);
str contains properly formatted JSON string (with quotation marks).
Related
I am trying to get a single nested value from a ResponseEntity but I am trying to do so without having to create a pojo for every possible item as this is a third party api response.
Example response.getBody() as it appears in Postman:
{
"message": "2 records found",
"records": [
{
"Account": {
"Id": "1",
"Name": "Foo Inc"
},
"CaseNumber": "200",
"Contact": {
"FirstName": "Foo",
"LastName": "Bar"
},
"Status": "In Progress",
"StatusMessage": "We are working on this."
},
{
"Account": {
"Id": "1",
"Name": "Foo Inc"
},
"CaseNumber": "100",
"Contact": {
"FirstName": "Foo",
"LastName": "Bar"
},
"Status": "Closed"
}
]
}
Basically, if I were in JS, I am looking for:
for(let record of res.body.records){
if(record && record.CaseNumber === "200"){
console.log(record.Status)
}
res.body.records[0].Status
Currently, they are are doing this to check if the response is empty:
ResponseEntity<Object> response = restTemplate.exchange(sfdcURL, HttpMethod.POST, entity, Object.class);
LinkedHashMap<Object, Object> resMap = (LinkedHashMap<Object, Object>) response.getBody();
List<Object> recordsList = (List<Object>) resMap.get("records");
if (recordsList.size() <= 0) { return error }
But I need to get the value of of "Status" and I need to do so without creating a pojo.
I appreciate any guidance on how I can do this in Java
UPDATE
So the response.getBody() is returned and when it is displayed in Postman, it looks like the pretty JSON shown above. However, when I do:
System.out.println(response.getBody().toString())
it looks like:
{message=2 Records Found, records=[{Account={Id=1, Name=Foo Inc}, CaseNumber=200, Contact={FirstName=Foo, LastName=Bar}, //etc
To make it worse, one of the fields appears in the console as follows (including linebreaks):
[...], Status=In Progress, LastEmail=From: noreply#blah.com
Sent: 2022-08-08 10:14:54
To: foo#bar.com
Subject: apropos case #200
Hello Foo,
We are working on your case and stuff
Thank you,
us, StatusMessage=We are working on this., OtherFields=blah, [...]
text.replaceAll("=", ":") would help some, but won't add quotations marks nor would it help separate that email block.
How can I so that the responses here like ObjectMapper and JSONObject can work?
You can either convert the string to valid json (not that trivial) and deserialise into a Map<String, Object>, or just pluck the value out of the raw string using regex:
String statusOfCaseNumber200 = response.getBody().toString()
.replaceAll(".*CaseNumber=200\\b.*?\\bStatus=([^,}]*).*", "$1");
This matches the whole string, captures the desired status value then replaces with the status, effectively "extracting" it.
The regex:
.*CaseNumber=200\b everything up to and including CaseNumber=200 (not matching longer numbers like 2001)
.*? as few chars as possible
\\bStatus= "Status=" without any preceding word chars
([^,}]*) non comma/curly brace characters
.* the rest
It's not bulletproof, but it will probably work for your use case so it doesn't need to be bulletproof.
Some test code:
String body = "{message=2 Records Found, records=[{Account={Id=1, Name=Foo Inc}, CaseNumber=200, Contact={FirstName=Foo, LastName=Bar}, Status=In Progress, StatusMessage=We are working on this.}, {Account={Id=1, Name=Foo Inc}, CaseNumber=100, Contact={FirstName=Foo, LastName=Bar}, Status=Closed}]";
String statusOfCaseNumber200 = body.replaceAll(".*CaseNumber=200\\b.*?\\bStatus=([^,}]*).*", "$1");
System.out.println(statusOfCaseNumber200); // "In Progress"
PLEASE DO NOT use Genson as Hiran showed in his example. The library hasn't been updated since 2019 and has many vulnerable dependencies!
Use Jackson or Gson.
Here how you can serialize a string into a Jackson JsonNode:
ObjectMapper mapper = new ObjectMapper();
String json = ...;
JsonNode node = mapper.readTree(json);
If you want to serialize a JSON object string into a Map:
ObjectMapper mapper = new ObjectMapper();
String json = ...;
Map<String, Object> map = mapper.readValue(json, HashMap.class);
You can read more about JsonNode here and a tutorial here.
You can use JSON-Java library and your code will look like this:
JSONObject jsonObject = new JSONObject(JSON_STRING);
String status = jsonObject.getJSONArray("records")
.getJSONObject(0)
.getString("Status");
System.out.println(status);
Or in a loop
JSONArray jsonArray = new JSONObject(jsonString).getJSONArray("records");
for(int i =0; i < jsonArray.length(); i++) {
String status = jsonArray
.getJSONObject(i)
.getString("Status");
System.out.println(status);
}
So the response.getBody() is returned and when it is displayed in Postman, it looks like the pretty JSON shown above. However, when I do:
...
text.replaceAll("=", ":") would help some, but won't add quotations
marks nor would it help separate that email block.
How can I so that the responses here like ObjectMapper and JSONObject
can work?
Firstly, Jackson is the default message converter which Spring Web uses under the hood to serialize and deserialize JSON. You don't need to introduce any dependencies.
Secondly, the process serialization/deserialization is handled by the framework automatically, so that in many cases you don't need to deal with the ObjectMapper yourself.
To emphasize, I'll repeat: in most of the cases in Spring you don't need to handle raw JSON yourself. And in the body of ResponseEntiry<Object> produced by the method RestTemplate.exchange() you have a LinkedHashMap in the guise of Object, it's not a raw JSON (if you want to know why it is a LinkedHashMap, well because that's how Jackson stores information, and it's a subclass of Object like any other class in Java). And sure, when you're invoking toString() on any implementation of the Map you'll get = between a Key and a Value.
So, the problem you've mentioned in the updated question is artificial.
If you want to deal with a Map instead of an object with properly typed properties and here's how you can do that:
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<LinkedHashMap<String, Object>> response = restTemplate.exchange(
sfdcURL, HttpMethod.POST, entity, new ParameterizedTypeReference<>() {}
);
Map<String, Object> resMap = response.getBody();
List<Object> recordsList = (List<Object>) resMap.get("records");
if (recordsList.isEmpty()) { ... }
If there are redundant lines in the Values which you want to trim, then as a remedy you can introduce a custom Jackson-module declaring a Deserializer which would handle leading/trailing white-space and new lines, described in this answer. Deserialize in the module would be applied by default, other options would require creating classes representing domain objects which you for some reasons want to avoid.
As Oliver suggested JsonNode seems to be the best approach. But, if I receive the ResponseEntity<Object>, I still cannot figure out a way to convert it to readable Json (and thus convert it to JsonNode), so I am still open to responses for that part.
I was able to get it to work by changing the ResponseEntity<Object> to ResponseEntity<JsonNode> so this is what I will be submitting for now:
ResponseEntity<JsonNode> response = restTemplate.exchange(sfdcURL,
HttpMethod.POST, entity, JsonNode.class);
JsonNode records = response.getBody().get("records");
String status = null;
String statusMessage = null;
for (JsonNode rec : records) {
if(rec.get("CaseNumber").asText().equals(caseNumber)) {
status = rec.get("Status").asText();
if(rec.has("StatusMessage")) {
statusMessage = rec.get("StatusMessage").asText();
}
} else {
statusMessage = "Invalid CaseNumber";
}
}
Because the overall method returns a ResponseEntity<Object> I then converted my strings to a HashMap and returned that:
HashMap<String, String> resMap = new HashMap<String, String>();
resMap.put("Status", status);
resMap.put("StatusMessage", statusMessage);
return new ResponseEntity<>(resMap, HttpStatus.OK);
This is not a perfect solution, but it works for now. Would still be better for exception handling if I could receive a ResponseEntity<Object> and then convert it to a JsonNode though. Thanks everyone for the responses!
My Java - Jersey framework REST API makes a call to another service which returns the following JSON response. I have logged the response from the child service in my logs, and I can see that the value of ErrorMessage contains a Unicode value like \u2019 instead of a single quote (').
{
"id": "SAMPLE",
"version": 1,
"status": {
"lastReceivedError": {
"ErrorDateTime": 1576588715,
"ErrorCode": "TEST3200",
"ErrorMessage": "We\u2019re sorry, the content is not available."
}
}
}
I have to map these values into my model and return as a JSON as well. I used GSON to convert the above JSON string into an object. And mapped the values from that object into my response object. My final outgoing JSON response is like below, wherein the single quote is appearing as question mark (?).
{
"MyResponse": {
"success": {
"lastReceivedError": {
"errorDateTime": "2019-12-17T13:18:35Z",
"errorCode": "TEST3200",
"errorMessage": "We?re sorry, the content is not available."
}
}
}
}
I believe there is something around encoding characters, but I am unable to fix the issue.
TL;DR
Seeing is not believing. It depends on the encoding in your environment.
Code snippet
Following code snippet shows to deserialize the JSON string (part of original response).
If the encoding of your environment is UTF-8, then Gson will convert it correctly without specifying encoding.
And if you already knew the original string was encoded with UTF-8, you will get different results if you view it with UTF-8 and ISO-8859-1.
String jsonStr = "{\"ErrorMessage\": \"We\u2019re sorry, the content is not available.\"}";
Gson gson = new Gson();
JsonObject data = gson.fromJson(jsonStr, JsonObject.class);
System.out.println(data.toString());
System.out.println(new String(jsonStr.getBytes("UTF-8"), "UTF-8"));
System.out.println(new String(jsonStr.getBytes("ISO-8859-1"), "UTF-8"));
Console output
{"ErrorMessage":"We’re sorry, the content is not available."}
{"ErrorMessage": "We’re sorry, the content is not available."}
{"ErrorMessage": "We?re sorry, the content is not available."}
I'm trying to create a web token using the jjwt library, but I can't figure out how to send an object as one of the claims. If I parse the object or manually create the string the entire string is coming through as the value instead of a separate JSON object. For instance, I want to send something that looks like this:
{
"iss": "NQoFK1NLVelFWOBQtQ8A",
"iat": 1511963669,
"user": {
"id": "exampleuser",
"email": "example#mail.com",
"name": "A User",
}
}
But all I've been able to create is:
{
"iss": "NQoFK1NLVelFWOBQtQ8A",
"iat": 1511963669,
"user": "{\"id\": \"example#mail.com\",\"email\": \"example#mail.com\",\"name\": \"A User\"}"
}
Welcome to StackOverflow!
This feature will be natively supported by JJWT when using Jackson in the upcoming JJWT 0.11.0 release (and you can read the docs for this feature too). But it is not available natively in 0.10.X and earlier.
Prior to 0.11.0, and assuming you're using Jackson, you'll have to do this yourself manually with the ObjectMapper:
// when creating:
User user = getUser();
ObjectMapper objectMapper = new ObjectMapper(); // or use an existing one
String json = objectMapper.writeValueAsString(user);
byte[] bytes = json.getBytes(StandardCharsets.UTF_8)
String base64 = Encoders.BASE64.encode(bytes);
String jws = Jwts.builder()
...
.claim("userJsonBase64", base64)
...
.compact();
//when parsing:
String userJsonBase64 = Jwts.parser()....parseClaimsJws(jws).getBody().get("userJsonBase64", String.class);
bytes = Decoders.BASE64.decode(userJsonBase64);
json = new String(bytes, StandardCharsets.UTF_8);
user = objectMapper.readValue(json, User.class);
Thank you for your answer, that will be a nice feature to have, once it's available. I wanted to follow up and post a workaround that I found, in case it helps anyone else in the meantime. I was able to create the JSON I needed using a Java HashMap (I found out the hard way that a Scala Map does not work) and then passing that as the value of the claim:
val user: util.Map[String, String] = new util.HashMap[String,
String]() user.put("id", email.value) user.put("email", email.value)
user.put("name", name.displayName)
...
val jws: String = Jwts.builder()
.claim("user", user)
.signWith(key).compact()
When I use Gson (JsonParser.parse) to decode the following:
{ "item": "Bread", "cost": {"currency": "\u0024", "amount": "3"}, "description": "This is bread\u2122. \u00A92015" }
The "currency" element is returned as a string of characters (and is not converted to a unicode character). Is there a setting or method in Gson that could help me?
If not, is there any way in Android to convert a string that contains one or more escaped character sequences (like "\u0024") to an output string with unicode characters (without writing my own and without using StringEscapeUtils from Apache)?
I'd like to avoid adding another library (for just one small feature).
Update
Looks like the server was double escaping the back slash in the unicode escape sequence. Thanks everyone for your help!
Is it only me or is it really more complicated than simply using TextView's setText() method? Anyhow, following is working just fine on my end with the given sample json (put the sample to assets and read it using loadJSONFromAsset()):
JsonParser parser = new JsonParser();
JsonElement element = parser.parse(loadJSONFromAsset());
JsonObject obj = element.getAsJsonObject();
JsonObject cost = obj.getAsJsonObject("cost");
JsonPrimitive sign = cost.get("currency").getAsJsonPrimitive();
TextView tv = (TextView)findViewById(R.id.dollar_sign);
tv.setText(sign.getAsString());
Gson returns "$". Something is wrong in your set up.
String s = "{ \"item\": \"Bread\", \"cost\": {\"currency\": "
+ "\"\\u0024\", \"amount\": \"3\"}, \"description\": "
+ "\"This is bread\\u2122. \\u00A92015\" }\n";
JsonElement v = new JsonParser().parse(s);
assertEquals("$", v.getAsJsonObject().get("cost").getAsJsonObject()
.get("currency").getAsString());
You can parse it as a hex number
char c = (char)Integer.parseInt(str.substring(2), 16);
I am new to using the json-simple library in Java and I've been through both the encoding and decoding samples. Duplicating the encoding examples was fine, but I have not been able to get the decoding ones to work with mixed type JSON.
One of my problems is that there are too many classes in the library which are not properly documented, and for which I do not have the source (in order to be able to read through and understand their purpose). Consequently, I am struggling to understand how to use a lot of these classes.
After reading this example:
String jsonText = "{\"first\": 123, \"second\": [4, 5, 6], \"third\": 789}";
JSONParser parser = new JSONParser();
ContainerFactory containerFactory = new ContainerFactory(){
public List creatArrayContainer() {
return new LinkedList();
}
public Map createObjectContainer() {
return new LinkedHashMap();
}
};
try {
Map json = (Map)parser.parse(jsonText, containerFactory);
Iterator iter = json.entrySet().iterator();
System.out.println("==iterate result==");
while(iter.hasNext()) {
Map.Entry entry = (Map.Entry)iter.next();
System.out.println(entry.getKey() + "=>" + entry.getValue());
}
System.out.println("==toJSONString()==");
System.out.println(JSONValue.toJSONString(json));
} catch(ParseException pe) {
System.out.println(pe);
}
from the json-simple official decoding tutorial, I tried to decode this JSON:
{
"stat":{
"sdr": "MAC address of FLYPORT",
"rcv": "ff:ff:ff:ff:ff:ff",
"time": "0000000000000",
"type": 0,
"subt": 0,
"argv": [
{"type": "6","val": "NetbiosName"},
{"type": "6","val": "MACaddrFlyport"},
{"type": "6","val": "FlyportModel"},
{"type": "1","val": id}
]
}
}
I am writing following code to decode:
String jsonString = "{\"stat\":{\"sdr\": \"aa:bb:cc:dd:ee:ff\",\"rcv\": \"aa:bb:cc:dd:ee:ff\",\"time\": \"UTC in millis\",\"type\": 1,\"subt\": 1,\"argv\": [{1,2},{2,3}]}}";
JSONObject jsonObject = new JSONObject(jsonString);
JSONObject newJSON = jsonObject.getJSONObject("stat");
System.out.println(newJSON);
But it doesn't work. Infact I was not able to get the unmodified example working either, and the original authors have not explained their code.
What is the easiest way to decode this JSON as shown?
This is the best and easiest code:
public class test
{
public static void main(String str[])
{
String jsonString = "{\"stat\": { \"sdr\": \"aa:bb:cc:dd:ee:ff\", \"rcv\": \"aa:bb:cc:dd:ee:ff\", \"time\": \"UTC in millis\", \"type\": 1, \"subt\": 1, \"argv\": [{\"type\": 1, \"val\":\"stackoverflow\"}]}}";
JSONObject jsonObject = new JSONObject(jsonString);
JSONObject newJSON = jsonObject.getJSONObject("stat");
System.out.println(newJSON.toString());
jsonObject = new JSONObject(newJSON.toString());
System.out.println(jsonObject.getString("rcv"));
System.out.println(jsonObject.getJSONArray("argv"));
}
}
The library definition of the json files are given here. And it is not same libraries as posted here, i.e. posted by you. What you had posted was simple json library I have used this library.
You can download the zip. And then create a package in your project with org.json as name. and paste all the downloaded codes there, and have fun.
I feel this to be the best and the most easiest JSON Decoding.
Well your jsonString is wrong.
String jsonString = "{\"stat\":{\"sdr\": \"aa:bb:cc:dd:ee:ff\",\"rcv\": \"aa:bb:cc:dd:ee:ff\",\"time\": \"UTC in millis\",\"type\": 1,\"subt\": 1,\"argv\": [{\"1\":2},{\"2\":3}]}}";
use this jsonString and if you use the same JSONParser and ContainerFactory in the example you will see that it will be encoded/decoded.
Additionally if you want to print your string after stat here it goes:
try{
Map json = (Map)parser.parse(jsonString, containerFactory);
Iterator iter = json.entrySet().iterator();
System.out.println("==iterate result==");
Object entry = json.get("stat");
System.out.println(entry);
}
And about the json libraries, there are a lot of them. Better you check this.
Instead of downloading separate java files as suggested by Veer, you could just add this JAR file to your package.
To add the jar file to your project in Eclipse, do the following:
Right click on your project, click Build Path > Configure Build Path
Goto Libraries tab > Add External JARs
Locate the JAR file and add
This is the JSON String we want to decode :
{
"stats": {
"sdr": "aa:bb:cc:dd:ee:ff",
"rcv": "aa:bb:cc:dd:ee:ff",
"time": "UTC in millis",
"type": 1,
"subt": 1,
"argv": [
{"1": 2},
{"2": 3}
]}
}
I store this string under the variable name "sJSON"
Now, this is how to decode it :)
// Creating a JSONObject from a String
JSONObject nodeRoot = new JSONObject(sJSON);
// Creating a sub-JSONObject from another JSONObject
JSONObject nodeStats = nodeRoot.getJSONObject("stats");
// Getting the value of a attribute in a JSONObject
String sSDR = nodeStats.getString("sdr");