I have a JSON map that contains a simple data type (String) and a complex data type. I have created a struct to handle this. Here it is:
public static class PayloadData {
String authkey;
PizzaPlaceTo pizzaPlace;
Map<String, List<String>> updates;
}
Now, I read the data in like this:
PayloadData payloadData = objectMapper.readValue(payload, PayloadData.class);
and it's giving me:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "authkey" (class pizzainthecloud.pizzaplace.controller.PizzaPlaceController$PayloadData), not marked as ignorable (0 known properties: ])
at [Source: {"authkey":"Don't Panic!","pizzaPlace":{"contactName":null,"customerId":null}; line: 1, column: 13] (through reference chain: pizzainthecloud.pizzaplace.controller.PizzaPlaceController$PayloadData["authkey"])
So if I'm reading this correctly, it's telling me there's no authkey in my receiving class to receive the authkey in the JSON data, right? Only there is.
Help, please.
So, it looks like that Jackson only works with beans and not with struct style classes. I changed my class to:
public static class PayloadData {
private String authkey;
private PizzaPlaceTo pizzaPlace;
private Map<String, List<String>> updates;
public String getAuthkey() {
return authkey;
}
public void setAuthkey(String authkey) {
this.authkey = authkey;
}
public PizzaPlaceTo getPizzaPlace() {
return pizzaPlace;
}
public void setPizzaPlace(PizzaPlaceTo pizzaPlace) {
this.pizzaPlace = pizzaPlace;
}
public Map<String, List<String>> getUpdates() {
return updates;
}
public void setUpdates(Map<String, List<String>> updates) {
this.updates = updates;
}
}
And that resolved the problem.
Related
I have the following Java class
I need to serialize it to json in the following way:
if the list(paymentTransactionReport field) is not null display it values -
{
"paymentTransactionResponse" : [
{},
{}
]
}
if the list is null I need to display the paymentTransactionReportError in json field with name 'paymentTransactionResponse', as in previous case. Example -
{
"paymentTransactionResponse" : {
----
//fields from PaymentTransactionReportError class
----
}
}
How can I do this?preferably without custom serializers.
If use just two annotations #JsonProperty with the same name and JsonInclude.NON_NULL as I did, I have this error: No converter found for return value of type:... Seems to be that is a error happened during serialization because of fields with the same name
One way you can achieve this is, using #JsonAnyGetter, Try this
public class TestDTO {
#JsonIgnore
List<String> paymentTransactionResponse;
#JsonIgnore
String paymentTransactionResponseError;
public List<String> getPaymentTransactionResponse() {
return paymentTransactionResponse;
}
public void setPaymentTransactionResponse(List<String> paymentTransactionResponse) {
this.paymentTransactionResponse = paymentTransactionResponse;
}
public String getPaymentTransactionResponseError() {
return paymentTransactionResponseError;
}
public void setPaymentTransactionResponseError(String paymentTransactionResponseError) {
this.paymentTransactionResponseError = paymentTransactionResponseError;
}
#JsonAnyGetter
public Map<String, Object> getData(){
Map<String, Object> map = new HashMap<String, Object>();
if(paymentTransactionResponse != null) {
map.put("paymentTransactionResponse", paymentTransactionResponse);
}else {
map.put("paymentTransactionResponse", paymentTransactionResponseError);
}
return map;
}}
I have a class with static variable
#Data
public final class Code {
private static Map<String, List<String>> codesForType = new HashMap<String, List<String>>() {{
put("code1", Arrays.asList("AVDF", "WREQ", "AWER"));
put("code2", Arrays.asList("SHYT", "DWEA", "XSSS", "AQWE"));
}};
public static List<String> getCodesByType(String type) {
return codesForType.get(type);
}
}
with following api
#GetMapping("/codes")
public Code getCodeForType() {
return new Code();
}
This is giving exception with message No converter found for return value of type: class com.model.Code.
Tried making the member as public but still has the same issue.
It works when I remove static keyword from private static Map<String, List<String>> codesForType
I could be missing a basic understanding of static keyword.
You don't need to create a new object as your methods are static.
you can simply do this :
#GetMapping("/codes")
public List<String> getCodeForType() {
// replace type with something you will receive in your request.
return Code.getCodesByType(type);
}
I am trying to convert the following JSON structure (part of a larger JSON object) to a POJO but getting the exception copied below (using Java/Jackson).
JSON
"outputKeys":
{"ABC":"gGyIioUr4Jfr5QiCm6Z==",
"DEF":"RxHfNyD2JyPOpG5tv3Jaj5g=="}
Java class
private class OutputKeys {
private String key;
private String value;
public OutputKeys(String key, String value) {
this.key = key;
this.value = value;
}
}
&
ObjectMapper mapper = new ObjectMapper();
mapper.readValue(jsonString, Test.class);
exception:
no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?
Test class has the OutputKeys as an attribute.
Any suggestions would be welcome. I have tried using a List of OutputKeys as well .
Update:
I have tried the following without success:
class OutputKeys {
public Map<String, String> keys;
///with constructor/setter/getters
}
&
class OutputKeys {
public List<OutputKey> keys;
///with constructor/setter/getters
public class OutputKey {
Map<String, String> outputs = new HashMap<>();
// tried this too:
// String key
//String value
}
You require below mentioned single class only, containing
All keys(ABC and DEF)
getters/setters
toString() which you'll use interact with JSON.
public class OutputKeys
{
private String ABC;
private String DEF;
public String getABC ()
{
return ABC;
}
public void setABC (String ABC)
{
this.ABC = ABC;
}
public String getDEF ()
{
return DEF;
}
public void setDEF (String DEF)
{
this.DEF = DEF;
}
#Override
public String toString()
{
return "ClassPojo [ABC = "+ABC+", DEF = "+DEF+"]";
}
}
Let me know if you require more details.
Since the keys were dynamic, I ended up deserializing the data using the iterator on the JsonNode:
jsonNode.get("outputKeys").iterator()
& then getting the relevant dynamic key information via the iterator.
I needed a similar tool for NodeJS. So that I can write tests on parts of a bigger model that was serialized (JSON).
So, if I need only "ABC":"gGyIioUr4Jfr5QiCm6Z==" or "XYZ":{"Hello": "My String", "Content": [1,2,3]}, the only property I care to test at the moment is:
var sutXYX = { Hello: "My String", Content: [ 1, 2, 2]};
I wrote this tool as a utility https://github.com/whindes/PojoScriptifyFromJSON
I'm looking to deserialize any unknown fields in a JSON object as entries in a Map which is a member of a POJO.
For example, here is the JSON:
{
"knownField" : 5,
"unknownField1" : "926f7c2f-1ae2-426b-9f36-4ba042334b68",
"unknownField2" : "ed51e59d-a551-4cdc-be69-7d337162b691"
}
Here is the POJO:
class MyObject {
int knownField;
Map<String, UUID> unknownFields;
// getters/setters whatever
}
Is there a way to configure this with Jackson? If not, is there an effective way to write a StdDeserializer to do it (assume the values in unknownFields can be a more complex but well known consistent type)?
There is a feature and an annotation exactly fitting this purpose.
I tested and it works with UUIDs like in your example:
class MyUUIDClass {
public int knownField;
Map<String, UUID> unknownFields = new HashMap<>();
// Capture all other fields that Jackson do not match other members
#JsonAnyGetter
public Map<String, UUID> otherFields() {
return unknownFields;
}
#JsonAnySetter
public void setOtherField(String name, UUID value) {
unknownFields.put(name, value);
}
}
And it would work like this:
MyUUIDClass deserialized = objectMapper.readValue("{" +
"\"knownField\": 1," +
"\"foo\": \"9cfc64e0-9fed-492e-a7a1-ed2350debd95\"" +
"}", MyUUIDClass.class);
Also more common types like Strings work:
class MyClass {
public int knownField;
Map<String, String> unknownFields = new HashMap<>();
// Capture all other fields that Jackson do not match other members
#JsonAnyGetter
public Map<String, String> otherFields() {
return unknownFields;
}
#JsonAnySetter
public void setOtherField(String name, String value) {
unknownFields.put(name, value);
}
}
(I found this feature in this blog post first).
I've tried everything but the solution is so ugly, I really want a straight forward answer on if this can be improved (and that means if I need to use a different implementation).
The problem lies in Map of Maps with GSON:
Gives me this response according to Firebug:
{"id": 2, "result": {"FirstWorld": {"FirstValue": 5, ... }, "SecondWorld":{"FirstValue": 5, ....}}, "error":null }
There are around 200 "Values", but only two "Worlds". This is what I have so far to parse it in my ControlService class:
public void RegisterValues( String [] Names, AsyncCallback<Map<String,RegisterValues>> callback);
public class RegisterValues
{
int FirstValue;
int SecondValue;
... And so on 200 times !!!
So I access the data like so:
service_.RegisterValues( Names, new AsyncCallback<ControlService.RegisterValues>()
{
public void onSuccess( GlibControlService.RegisterValues result )
{
String test = "FirstValue";
String message="Result for channel 1 is ";
for( String Name : result.keySet() ) message+=Name+"="+result.get(Name);
But as you can see, this is going to be really long. The other problem is some of the "Values" have ampersands in them, which means I can't use them in this method e.g;
#SerializedName("One&Two") // Ampersand not allowed in name
int OneTwo; //gives Invalid JSON response apparently
Does anyone have a better method of doing this?
EDIT Code that works:
private ControlService service_;
service_.connectedNames( new String[0], new AsyncCallback<String[]>() {
public void onSuccess( String[] result)
{
List_.removeItem(0);
Names = result;
for( String Name : result ) {
List_.addItem(Name);
}
List_.setEnabled( true );
}
public void onFailure(Throwable why)
{
List_.removeItem(0);
List_.addItem( "Server error!" );
}
});
Then in my ControlService Class, I have this:
#RpcImpl(version=RpcImpl.Version.V2_0,transport=RpcImpl.Transport.HTTP_POST)
public interface ControlService extends RemoteJsonService
{
public void connectedNames( String [] Names, AsyncCallback<String[]> callback );
This works perfectly.
I tried doing it a very similar way by adding this in my ControlService:
public void RegisterValues( String [] Names, AsyncCallback<Map< String,Map<String, Integer>> callback);
And so on, making sure the Map<String, Map<String, Integer>> was also in the onSucess() part of the structure too. This however caused my program to crash. It seemed to me that it doesn’t like nested maps. However GSON will automatically parse in objects if the member name matches the JSON field. So I used that RegisterValues to automatically force GSON to parse this.
Stack trace:
[ERROR] Errors in 'generated://AF9BA58B045D92E7896CD657C9CC5FAF/example/client/ControlService_JsonProxy.java'
[ERROR] Line 18: INSTANCE cannot be resolved or is not a field
See snapshot: /var/folders/pf/56b3mznn35gg741rlsqq424m0000gp/T/example.client.ControlService_JsonProxy2948240672238331252.java
This is why I think GSON can't automatically parse Nested Maps using AsyncCallback. It may be better to do my HTTP call more in line with what you suggested below.
The best way is to wrap the response you posted into one class.
{"id": 2, "result": {"FirstWorld": {"FirstValue": 5, ... }, "SecondWorld":{"FirstValue": 5, ....}}, "error":null }
import java.util.Map;
public class Response {
private int id;
private Map<String, Map<String, Integer>> result;
private String error;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Map<String, Map<String, Integer>> getResult() {
return result;
}
public void setResult(Map<String, Map<String, Integer>> result) {
this.result = result;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
}
I've tested with this and it runs fine.
static String json = "{\"id\":2,\"result\":{\"FirstWorld\":{\"FirstValue\":5,\"SecondValue\":6},\"SecondWorld\":{\"FirstValue\":5,\"SecondValue\":6}},\"error\":null}";
public static void main(String[] args) {
Gson g= new Gson();
Response b=g.fromJson(json, Response.class );
System.out.println(g.toJson(b));
}
If you need to have the RegisterValues class declared,
you only need to replace the Map<String, Integer> of result with this class, probably extending Map.