Java: How to Make certain JSON field not to be printed - java

I have a Document request that I need to send to a REST service endpoint:
#Data // lombok
public class Document {
private String name;
private String location;
private String content; // a base64 encoded byte array
}
I have a utility class that I can use to log the entire JSON object in the log file. So in case an exception is thrown at runtime, the code will log at the ERROR level the request that causes the exception, something like this:
{
"name": "file1.pdf",
"location" : "/root/folder/",
"content" : "JVBERi0xLjQKJdP0zOEKJxxxxxxxxxxxxxxxxxxxx"
}
The problem is in the "content" field. If the file is very large, the "content" field will fill up the log file very quickly when an exception happens at runtime. So how do I make the "content" field not to be printed in the log file? I thought about using #JsonIgnore, but will it make the "content" field to be ignored when the request is passed to the endpoint? I am not using Gson.

You can use JacksonMixin:
#Data
public class Document {
private String name;
private String location;
private String content;
}
public static abstract class ContentIgnoreMixin {
#JsonIgnore
private String content;
}
// same as ContentIgnoreMixin, but interface using getter
public interface class ContentIgnoreMixin2 {
#JsonIgnore
String getContent();
}
and later:
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(Document.class, ContentIgnoreMixin.class); // or ContentIgnoreMixin2 - no difference
String jsonWithoutContent = mapper.writeValueAsString(objectWithContent);

Related

Cannot deserialize value of type `java.lang.String` from Array value from mockmvc

public class ModelDTO implements Serializable {
private Long id;
private String datasetName;
#JsonRawValue
private String json;
}
Post API is working fine from postman or swagger if I send the following body.
{
"id": 1,
"datasetName": "Insurance",
"json" : "[{\"ClassName\":\"AAAA\",\"Fields\":[[\"cdsa\",\"csa\"],[\"ca\"]]},{\"ClassName\":\"ca\",\"Fields\":[null]}]"
}
But MockMVC test case is giving follwing error in Spring boot project
Bad Request: JSON parse error: Unexpected character ('C' (code 67)): was expecting comma to separate Object entries; nested exception is com.fasterxml.jackson.core.JsonParseException: Unexpected character ('C' (code 67)): was expecting comma to separate Object entries
at [Source: (PushbackInputStream); line: 1, column: 89]
mockMvc.perform(post(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(new ObjectMapper().writeValueAsString(ModelDTO))).andExpect(status().isCreated());
In my case I had incorrect Type definition.
e.g.
public class ModelDTO implements Serializable {
private Long id;
private String datasetName;
private String part;
}
So I had to correct it as:
public class ModelDTO implements Serializable {
private Long id;
private String datasetName;
private List<String> part;
}
That solved my issue.
I believe you don't actually need #JsonRawValue so try removing it:
public class ModelDTO implements Serializable {
private Long id;
private String datasetName;
private String json;
}
my json were not properly set. it requires backword slash for parsing.
I have added extra two backword slash like '\' and now it is working.
private static final String DEFAULT_JSON = "\"[{\\\"ClassName\\\":\\\"Health Aditya Birla\\\",\\\"Fields\\\":[[\\\"Insured Person's Details\\\",\\\"Gender\\\",\\\"Member ID\\\"],[\\\"Policy Details\\\",\\\"Insured Person's Details\\\"],[\\\"Premium Certificate\\\"]]},{\\\"ClassName\\\":\\\"Health Care\\\",\\\"Fields\\\":[[\\\"Details of Insured\\\",\\\"Relationship\\\",\\\"Date Of Birth\\\"],[\\\"Mobile No\\\"],[\\\"Gross Premium\\\",\\\"Goods & Services Tax\\\"]]}]\"";
I have referred following link to solve this issue.
enter link description here

Parsing JSON properties into list of strings property

I use Java Spring in my project.
I need to parse JSON into java class.
Example of the JSON:
[
{
"Id":"aaa1"
"Data1":"test11"
"Data2":"test12"
"url1": "https://...someimg11.png",
"url2": "https://...someimg12.png",
},
{
"Id":"aaa2"
"Data1":"test21"
"Data2":"test22"
"url1": "https://...someimg21.png",
"url2": "https://...someimg22.png",
},
{
"Id":"aaa3"
"data1":"test31"
"data2":"test32"
"url1": "https://...someimg31.png",
"url2": "https://...someimg32.png",
}
]
And here the Java class that json above should be parsed to:
class Info{
#JsonProperty("Id")
public String id;
#JsonProperty("Data1")
public String data1;
#JsonProperty("Data2")
public String data2;
//parse to here url1 and url2 properties from json doc
public List<String> urls;
}
As you can see I have no issues to parse properties, except the last properties in JSON url1 and url2,
the properties of JSON file url1 and url2 should be parsed into URLs property of type List.
My question is, how can I parse the two properties of JSON inside the single property in the class of type list of strings?
UPDATE
I can use #JsonAnySetter annotation as suggested on one of the answers, but in this case, I will need to initialize the urls property, otherwise, I get this error on parsing:
Cannot invoke "java.util.List.add(Object)" because "this.urls" is null
In case if I use #JsonAnySetter annotation how can I init urls property?
You can do it using #JsonAnySetter annotation and I don't if there is any easier way of using patterns or wildcards
class Info{
#JsonProperty("Id")
public String id;
#JsonProperty("Data1")
public String data1;
#JsonProperty("Data2")
public String data2;
//parse to here url1 and url2 properties from json doc
public List<String> urls = new ArrayList<>();
#JsonAnySetter
public void add(String key, String value) {
if(key.equals("url1") || key.equals("url2")) {
urls.add(value);
}
}
}

How to call feild name in postman request body defined in application.properties

I have declared two enums in two separate application.properties file and I have also created a class for constant values.
Application.properties file1
EnumProperty.Provider=Provider1
url1=http://localhost:8080/some/url
Application.properties file2
EnumProperties.Provider=Provider2
url1=http://localhost:8080/some/urlss
CommonConstant.class
public final String PROVIDER1 = "PROVIDER1";
public final String PROVIDER2="PROVIDER2";
Also I have ConfigurationReader.class
#Value("{EnumProperty.Provider}")
private String providerOne;
#Value("{EnumProperties.Provider}")
private String providerTwo;
Now I have a service class where I'm suppose to call one class out of two based on which provider I'm calling.Which is as follows
public ResponseObject service(CommonRequestFeilds commonRequestFeilds) {
if (configurationReader.getProviderOne().equals(CommonConstant.PROVIDER1)) {
classOneServiceImp.someMethodOfClass1(commonRequestFeilds);
}
else if (configurationReader.getProviderTwo().equals(CommonConstant.PROVIDER2)) {
classTwoServiceImp.someMethodOfClass2(commonRequestFeilds);
}
return null;
}
Also CommonRequestFeild.class
private String email;
private String firstName;
private String lastName;
Postman RequestBody
{
"email":"john#example.com",
"firstName":"John",
"lastName":"Doe",
}
Now in postman request body I'm sending the commonRequestFeilds values.But where I'm stuck is how do I tell in my postman request body which provider I'm trying to call from ConfigurationReader class.Should I declare ConfigurationReader class in CommonRequestFeild class? Please help.

Converting a JSON with array of objects to proper Java object

I have a simple Spring Boot project in which a scheduler periodically consumes a RESTful API and converts the incoming JSON file.
The JSON file is actually an array of Objects with some Keys and Values:
[
{"CoID":1,"CoName":"کشاورزی و دامپروری مگسال","CoNameEnglish":"MagsalAgriculture & Animal Husbandry Co.","CompanySymbol":"MAGS","CoTSESymbol":"زمگسا","GroupID":1,"GroupName":"كشاورزی و دامپروری","IndustryID":1,"IndustryName":"كشاورزی، دامپروری و خدمات وابسته به آن","InstCode":"5054819322815158","TseCIsinCode":"IRO1MAGS0006","TseSIsinCode":"IRO1MAGS0001","MarketID":1,"MarketName":"بورس"},
{"CoID":2,"CoName":"ذغالسنگ نگین طبس","CoNameEnglish":"Negin Tabas Lignite Co.","CompanySymbol":"TBAS","CoTSESymbol":"کطبس","GroupID":2,"GroupName":"استخراج و انبار ذغال سنگ سخت","IndustryID":2,"IndustryName":"استخراج ذغال سنگ","InstCode":"8977369674477111","TseCIsinCode":"IRO1TBAS0004","TseSIsinCode":"IRO1TBAS0001","MarketID":1,"MarketName":"بورس"},{"CoID":3,"CoName":"معدنی و صنعتی چادرملو","CoNameEnglish":"Chadormalu Mining & Industrial Co.","CompanySymbol":"CHML","CoTSESymbol":"کچاد","GroupID":3,"GroupName":"استخراج سنگ معدن های فلزی آهنی","IndustryID":3,"IndustryName":"استخراج كانه های فلزی","InstCode":"18027801615184692","TseCIsinCode":"IRO1CHML0000","TseSIsinCode":"IRO1CHML0001","MarketID":1,"MarketName":"بورس"}
...
]
I have a class called Company with similar fields to one of objects in the array within the JSON file:
#JsonIgnoreProperties(ignoreUnknown = true)
public class Company {
private int CoID;
private String CoName;
private String CoNameEnglish;
private String CompanySymbl;
private String CoTSESymbl;
private int GroupID;
private String GroupName;
private int IndustryID;
private String IndustryName;
private String IndustryCode;
private String TseCIsinCode;
private String TseSIsinCode;
private int MarketID;
private String MarketName;
// And proper getters, setters and constructor //
I also created a wrapping class called CompanyList:
public class CompanyList {
private ArrayList<Company> companyList;
public ArrayList<Company> getCompanyList() {
return companyList;
}
public void setCompanyList(ArrayList<Company> companyList) {
this.companyList = companyList;
}
public CompanyList() {
}
#Override
public String toString() {
return "CompanyList [companyList=" + companyList + "]";
}
}
I have tried three different ways to fulfill this requirement:
First:
Object[] forNow = restTemplate.getForObject("somewhere", Object[].class);
List<Object> cp= Arrays.asList(forNow);
This one works properly.
Second:
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<List<Company>> response = restTemplate.exchange(
"somewhere",
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<Company>>(){});
List<Company> companies = response.getBody();
log.info(companies.toString());
This one is compiled successfully but returns null and 0 in all fields.
Third:
CompanyList cp = restTemplate.getForObject("somewhere", CompanyList.class);
log.info(cp.getCompanyList().toString());
This one raises an exception:
Error while extracting response for type [class ir.pisys.rest.CompanyList] and content type [application/json;charset=utf-8];
nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize instance of ir.pisys.rest.CompanyList out of START_ARRAY token;
nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of ir.pisys.rest.CompanyList out of START_ARRAY token
So I have some questions here:
1- Is the first approach an optimized one? (Compared to others)
2- How can I fix the two other approaches?
The second and third approaches should work fine.
You need to check your json response structure.
You could use following jsons for tests (they work with your code):
Second approach:
[{"tseCIsinCode":null,"tseSIsinCode":null,"coName":"n1","industryID":0,"coID":0,"coNameEnglish":null,"companySymbl":null,"coTSESymbl":null,"groupID":0,"groupName":null,"industryName":null,"industryCode":null,"marketID":0,"marketName":null},{"tseCIsinCode":null,"tseSIsinCode":null,"coName":"n2","industryID":0,"coID":0,"coNameEnglish":null,"companySymbl":null,"coTSESymbl":null,"groupID":0,"groupName":null,"industryName":null,"industryCode":null,"marketID":0,"marketName":null}]
Third:
{"companyList":[{"coName":"n1","coID":0,"coNameEnglish":null,"companySymbl":null,"coTSESymbl":null,"groupID":0,"groupName":null,"industryID":0,"industryName":null,"industryCode":null,"tseCIsinCode":null,"tseSIsinCode":null,"marketID":0,"marketName":null},{"coName":"n2","coID":0,"coNameEnglish":null,"companySymbl":null,"coTSESymbl":null,"groupID":0,"groupName":null,"industryID":0,"industryName":null,"industryCode":null,"tseCIsinCode":null,"tseSIsinCode":null,"marketID":0,"marketName":null}]}
Update:
Second approach fix:
Change your json fields name - "CoName" -> "coName", "CoID" -> "coID" and so on. After that changes it will work pirfectly.
Third approach fix:
Wrap your json with "{\"companyList\":[...]
And change fields name as for second approach
Second Update
If you can't change json from response. You could use mapping in your Company class
#JsonProperty("CoName")
private String CoName;

Polymorphic Deserialization Issue with Jackson

I have the following classes that I want to deserialize a JSON string to using Jackson.
PushNotificationMessage.java
public class PushNotificationMessage {
#JsonProperty("device_info")
private DeviceInfo deviceInfo;
private String content;
//getters & setters
}
DeviceInfo.java
public class DeviceInfo {
#JsonProperty(value = "device_type")
private String deviceType;
//getters & setters
}
IOSDeviceInfo.java
public class IOSDeviceInfo extends DeviceInfo {
#JsonProperty(value = "device_id")
private String deviceId;
private String arn;
#JsonProperty(value = "user_data")
private String userData;
//getters & setters
}
WebDeviceInfo.java
public class WebDeviceInfo extends DeviceInfo {
private String endpoint;
private String key;
private String auth;
//getters & setters
}
I have the following JSON content that I want to deserialize:
{
"device_info": {
"endpoint": "https://android.googleapis.com/gcm/send/blah",
"key": "blahkey",
"auth": "blahauth",
"device_type": "web"
},
"content": "Notification content"
}
I simply use ObjectMapper to try to perform the deserialization as such.
final ObjectMapper objectMapper = new ObjectMapper();
final PushNotificationMessage message = objectMapper.readValue(jsonString, PushNotifictionMessage.class);
When I do this I get:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "endpoint" (class com.blah.DeviceInfo), not marked as ignorable (one known property: "device_type"])
How can I get Jackson to recognize that it needs to be mapped to a WebDeviceInfo instance, instead of trying to map it to the DeviceInfo superclass, which does not have the endpoint field?
I've tried playing with #JsonTypeInfo and #JsonSubTypes annotations in my different classes, but I can find no good examples of how to use them.
EDIT: I added the #JsonDeserialize(using = DeviceInfoDeserializer.class) annotation to my DeviceInfo class, and created the following DeviceInfoDeserializer.
public class DeviceInfoDeserializer extends JsonDeserializer<DeviceInfo> {
private static final String DEVICE_TYPE = "device_type";
private static final String WEB = "web";
private static final String IOS = "ios";
#Override
public DeviceInfo deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException {
final ObjectMapper objectMapper = (ObjectMapper) jsonParser.getCodec();
final ObjectNode root = objectMapper.readTree(jsonParser);
if (root.has(DEVICE_TYPE)) {
final JsonNode jsonNode = root.get(DEVICE_TYPE);
if (jsonNode.asText().equalsIgnoreCase(WEB)) {
return objectMapper.readValue(root.toString(), WebDeviceInfo.class);
} else if (jsonNode.asText().equalsIgnoreCase(IOS)) {
return objectMapper.readValue(root.toString(), IOSDeviceInfo.class);
}
}
throw deserializationContext.mappingException("Failed to de-serialize device info, as device_type was not \"web\" or \"ios\"");
}
}
Now, I get a different error when attempting to deserialize my PushNotificationMessage JSON:
java.lang.StackOverflowError: null
at com.fasterxml.jackson.databind.deser.std.BaseNodeDeserializer.deserializeObject(JsonNodeDeserializer.java:210)
at com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer.deserialize(JsonNodeDeserializer.java:69)
at com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer.deserialize(JsonNodeDeserializer.java:15)
at com.fasterxml.jackson.databind.ObjectMapper._readValue(ObjectMapper.java:3770)
at com.fasterxml.jackson.databind.ObjectMapper.readTree(ObjectMapper.java:2207)
at com.blah.serialization.DeviceInfoDeserializer.deserialize(DeviceInfoDeserializer.java:25)
at com.blah.serialization.DeviceInfoDeserializer.deserialize(DeviceInfoDeserializer.java:16)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3798)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2842)
... (above trace repeated many times)
EDIT: Just needed to add #JsonDeserialize(as = WebDeviceInfo.class) and #JsonDeserialize(as = IOSDeviceInfo.class) to my subclasses, now it works as expected. Big thank you to #Luciano van der Veekens.
Jackson is not aware of polymorphism, it just tries to create an instance of the concrete DeviceInfo class.
However, you can implement a custom deserializer that programmatically parses the device info JSON and knows when to instantiate one of the subclasses due to the uniqueness of some fields such as endpoint.
https://fasterxml.github.io/jackson-databind/javadoc/2.2.0/com/fasterxml/jackson/databind/annotation/JsonDeserialize.html
#JsonDeserialize(using = DeviceInfoDeserializer.class)
public class DeviceInfo {
}
An example can be found here: http://sunilkumarpblog.blogspot.nl/2015/12/javajson-polymorphic-serialization-de.html

Categories

Resources