Issues when reading JSON values from POJO with ObjectMapper - java

I want to parse JSON String into POJO class but I am getting error.
And
can we return two values from a method in java
String data
String PostcreatedMessage = "..."; // see JSON value below
{
"home_page":"/desk",
"message":"Logged In",
"full_name":"Shoaib Shaikh"
}
Main.class
LoginR loginR=new LoginR();
ObjectMapper mapper=new ObjectMapper();
loginR=mapper.readValue(PostcreatedMessage,LoginR.class);
System.out.println(loginR.getHomePage());
System.out.println(loginR.getMessage());
System.out.println(loginR.getFullName());
parsejacker.class
public class parsejacker
{
LoginR loginR=null;
String jsonurl;
public parsejacker(String jsonurl) {
super();
this.jsonurl = jsonurl;
}
public void ParseLogin() throws JsonParseException,JsonMappingException,IOException
{
System.out.println(jsonurl+"this parselogin");
ObjectMapper mapper=new ObjectMapper();
loginR=mapper.readValue(jsonurl,LoginR.class);
System.out.println(loginR.getHomePage());
System.out.println(loginR.getMessage());
System.out.println(loginR.getFullName());
}
}
LoginR.class
public class LoginR
{
private String homePage;
private String message;
private String fullName;
public String getHomePage(){
return homePage;
}
public void setHomePage(String input){
this.homePage = input;
}
public String getMessage(){
return message;
}
public void setMessage(String input){
this.message = input;
}
public String getFullName(){
return fullName;
}
public void setFullName(String input){
this.fullName = input;
}
}
I am Getting these errors.
(I think the error is in the PostcreatedMessage string.
How to overcome "home_page":"/desk"? --> this backslash error i.e delimiting or ignore this char)
Exception in thread "main" org.codehaus.jackson.map.exc.UnrecognizedPropertyException:
Unrecognized field "home_page" (Class org.greenshoaib.greenshaikh.login.model.LoginR), not marked as ignorable
at [Source: java.io.StringReader#4738a206; line: 1, column: 15]
(through reference chain: org.greenshoaib.greenshaikh.login.model.LoginR["home_page"])
at org.codehaus.jackson.map.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:53)
at org.codehaus.jackson.map.deser.StdDeserializationContext.unknownFieldException(StdDeserializationContext.java:267)
at org.codehaus.jackson.map.deser.std.StdDeserializer.reportUnknownProperty(StdDeserializer.java:673)
at org.codehaus.jackson.map.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:659)
at org.codehaus.jackson.map.deser.BeanDeserializer.handleUnknownProperty(BeanDeserializer.java:1365)
at org.codehaus.jackson.map.deser.BeanDeserializer._handleUnknown(BeanDeserializer.java:725)
at org.codehaus.jackson.map.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:703)
at org.codehaus.jackson.map.deser.BeanDeserializer.deserialize(BeanDeserializer.java:580)
at org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.java:2732)
at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1863)
at org.greenshoaib.greenshaikh.rest.client.RestAPIClient.main(RestAPIClient.java:72 )

The problem is that your JSON contains a field "home_page" but in your pojo it's called homePage (same for "full_name"). Jackson doesn't know the two are equal so you need to help it, e.g. by adding #JsonProperty("home_page") to homePage, i.e. like this (and remember to do that same for fullName):
#JsonProperty("home_page")
private String homePage;

Try your method with the following json String :
PostcreatedMessage {
"homePage":"/desk",
"message":"Logged In",
"fullName":"Shoaib Shaikh"
}

Your problem here is that the ObjectMapper cannot recognize the properties of your LoginR class by name, e.g. homePage != home_page.
You can annotate those properties to match their expected JSON key.
Here's a minimal example:
public class LoginR {
#JsonProperty(value="home_page")
private String homePage;
#JsonProperty(value="full_name")
private String fullName;
// etc. the rest of your POJO
}
... somewhere else...
String json = "{\"home_page\":\"/desk\",\"message\":\"Logged In\",\"full_name\":\"Shoaib Shaikh\"}";
ObjectMapper mapper=new ObjectMapper();
LoginR loginR = mapper.readValue(json,LoginR.class);
System.out.println(loginR.getHomePage());
System.out.println(loginR.getMessage());
System.out.println(loginR.getFullName());
Output
/desk
Logged In
Shoaib Shaikh

Related

How do I store contents of a parsed JSON message into string variables based on keywords?

I need help storing text from a parsed JSON message into variables based on keywords. What I mean by this is, I have a JSON message that I have parsed once, and within that message, it returns things like:
Name: Kobe Bryant<br/>
Company: Lakers<br/>
Email: kobe#lakers.com<br/>
etc.
I want to be able to look at this block of test, see that it says "Name: Kobe Bryant," and store Kobe Bryant into a string variable called "name" and so on. How can I handle this?
public class ParseTest {
public static void main(String[] args) throws Exception {
String name;
String company;
String email;
String phoneNumber;
String projectType;
String contactBy;
String timeFrame;
String message;
ObjectMapper mapper = new ObjectMapper();
mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
MainParser mp = mapper.readValue(new File("/Users/kane/VersionControl/LambdaTest/src/InfoBox.txt"), MainParser.class);
if ("blah".equals(mp.getTopicArn())) {
//Send to proposal table
System.out.println(mp.getSubject());
System.out.println(mp.getMessage());
} else if ("blah blah".equals(mp.getTopicArn())) {
//Send to infoBox table
System.out.println(mp.getMessage());
}
}
}
The JSON that I'm parsing is:
{
"Subject" : "Proposal Request (sent Wed May 22 2019 14:47:49 GMT-0400 (Eastern Daylight Time))",
"Message" : "Name: Kobe Bryant\nCompany: Lakers\nEmail: kobe#lakers.com"
}
Here's the POJO class:
private String Subject;
private String Message;
public String getSubject() {
return Subject;
}
public void setSubject(String subject) {
Subject = subject;
}
public String getMessage() {
return Message;
}
public void setMessage(String message) {
Message = message;
}
You have a JSON payload where one of values contains data in key:value pairs split by new line \n. We need to create two POJO classes: Message for root JSON and Person - internal person data. These two classes could look like this:
class Message {
private String subject;
private String message;
// getters, setters, toString
}
class Person {
private String name;
private String company;
private String email;
// ... more properties
// getters, setters, toString
}
We can parse your JSON payload as below:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import java.io.File;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class JsonApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE);
Message message = mapper.readValue(jsonFile, Message.class);
String[] lines = message.getMessage().split("\n");
Map<String, String> properties = Stream.of(lines)
.map(i -> i.split(":"))
.filter(i -> i.length == 2)
.collect(Collectors.toMap(i -> i[0].trim(), i -> i[1].trim()));
Person person = mapper.convertValue(properties, Person.class);
System.out.println(person);
}
}
Above example prints for your JSON payload:
Person{name='Kobe Bryant', company='Lakers', email='kobe#lakers.com'}
As you can see, we created Map from internal data and used convertValue method to convert Map to Person class.
See also:
Java 8 – Convert List to Map
How to deserialize a Map into POJO?
How to serialize to java.util.List and java.util.Map with Jackson

JSON deserialize property including $

Having a JSON with a property like this which i try to deserialze with jackson:
{ "$fooid" : "yfdfjjhkjhkljhd768622323jjj" }
Problem here is, i'm not able to read the key "$fooid".
I tried to annotate the property with
#JsonAlias("$fooid")
private String fooid;
also as
#JsonProperty("$fooid")
private String fooid;
Both variants won't work, the property fooid is always null.
How to deserialize it in Java? Any hints?
I am not sure how are you doing It, but It is working for me on version 2.0.0 doing this.
public class JSonAliasWithSpecialCharacters {
public static void main(String[] args) throws IOException {
String json="{ \"$fooid\" : \"yfdfjjhkjhkljhd768622323jjj\" }";
ObjectMapper mapper = new ObjectMapper();
JsonFooid fooid = mapper.readValue(json, JsonFooid.class);
System.out.println("read the foodid:"+fooid.getFooid());
}
}
public class JsonFooid {
#JsonProperty("$fooid")
private String fooid;
public String getFooid() {
return fooid;
}
public void setFooid(String fooid) {
this.fooid = fooid;
}
}

How to unit test enum composed via #JsonCreator?

I am using Jackson to deserialize a JSON string into an enum.
public enum RoomType {
SHARED("shared"),
PRIVATE("private");
private String value;
RoomType(String value) {
this.value = value;
}
#JsonCreator
public static RoomType fromJson(final String jsonValue) {
for (RoomType type : values()) {
if (type.value.equals(jsonValue)) {
return type;
}
}
return null;
}
#JsonValue
#Override
public String toString() {
return value;
}
}
I want to unit test the different edge cases:
#RunWith(JUnit4.class)
public class RoomTypeTest {
private final ObjectMapper mapper = new ObjectMapper();
#Test
public void fromJsonWithShared() throws Exception {
String json = "{\"roomType\":\"shared\"}";
RoomType type = mapper.readValue(json, RoomType.class);
assertThat(type).isEqualTo(RoomType.SHARED);
}
}
The test fails. When I debug I see that jsonValue is null when RoomType.fromJson is invoked. Seems like that Jackson does not pick up the value from the JSON string.
Related examples
EnumCreatorTest929.java
I think Jackson doesn't know what value to pass to that fromJson method. Try adding #JsonProperty:
#JsonCreator
public static RoomType fromJson(#JsonProperty("roomType") final String jsonValue) {
....
}

Jackson deserialize Enums with multiple names

I have problems deserializing Enums that have multiple names for a value. Here is an example: Info is a Java class that inside has an enum with multiple names:
public class Info {
//...
private ContainerFormat format;
}
// ContainerFormat.java:
public enum ContainerFormat {
// ....
MP4("mp4", "mpeg4"),
UNKNOWN("null");
private String name;
private List<String> others;
ContainerFormat(String name) {
this.name = name;
}
/** The service does not always return the same String for output formats.
* This 'other' string fixes the deserialization issues caused by that.
*/
ContainerFormat(String name, String... others) {
this.name = name;
this.others = new ArrayList<String>();
for (String other : others) {
this.others.add(other);
}
}
#JsonValue
#Override
public String toString() {
return name;
}
public List<String> otherNames() {
return others;
}
#JsonCreator
public static ContainerFormat fromValue(String other) throws JsonMappingException {
for (ContainerFormat format : ContainerFormat.values()) {
if (format.toString().equalsIgnoreCase(other)) {
return format;
}
if (format.otherNames() != null && format.otherNames().contains(other)) {
return format;
}
}
return UNKNOWN;
}
}
The problem is when I deserialize something that contains "mpeg4" instead of mp4 I get this error:
com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not construct instance of com.foo.ContainerFormat from String value 'mpeg4': value not one of declared Enum instance names
at [Source: N/A; line: -1, column: -1] (through reference chain: com.foo.Info["format"])
at com.fasterxml.jackson.databind.exc.InvalidFormatException.from(InvalidFormatException.java:55)
at com.fasterxml.jackson.databind.DeserializationContext.weirdStringException(DeserializationContext.java:650)
at com.fasterxml.jackson.databind.deser.std.EnumDeserializer.deserialize(EnumDeserializer.java:85)
at com.fasterxml.jackson.databind.deser.std.EnumDeserializer.deserialize(EnumDeserializer.java:20)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:375)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:98)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:308)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121)
at com.fasterxml.jackson.databind.ObjectMapper._readValue(ObjectMapper.java:2769)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1478)
at com.fasterxml.jackson.databind.ObjectMapper.treeToValue(ObjectMapper.java:1811)
Any pointers on how to fix this?
TIA
I found a good solution based on Florin's answer:
the correct configuration with jackson 2.7.0-rc2 (and probably also before)
private ObjectMapper createObjectMapper() {
final ObjectMapper mapper = new ObjectMapper();
// enable toString method of enums to return the value to be mapped
mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
mapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
return mapper;
}
In your enum you just have to override the toString() method:
public enum EXAMPLE_TYPE {
START("start"),
MORE("more");
// the value which is used for matching
// the json node value with this enum
private final String value;
SectionType(final String type) {
value = type;
}
#Override
public String toString() {
return value;
}
}
You don't need any annotations or custom deserializers.
Get rid of String name and List<String> other and instead have just one field - List<String> names and serialize the single getter with #JsonValue
public enum ContainerFormat {
// ....
MP4("mp4", "mpeg4"),
UNKNOWN("null");
private List<String> names;
ContainerFormat(List<String> names) {
this.names = new ArrayList<String>(names);
}
#JsonValue
public List<String> getNames()
{
return this.names;
}
#JsonCreator
public static ContainerFormat getContainerFromValue(String value) throws JsonMappingException {
for (ContainerFormat format : ContainerFormat.values()) {
if(format.getValues().contains(value))
return format;
}
return UNKNOWN;
}
Alternatively, if you choose to keep your existing code, you could try annotating otherValues() with #JsonValue
Well, I found a workaround: one of these flags does the right thing and allows me to read that mpeg4 back in:
mapper.configure(org.codehaus.jackson.map.SerializationConfig.Feature.WRITE_NULL_PROPERTIES, false);
mapper.configure(org.codehaus.jackson.map.SerializationConfig.Feature.WRITE_ENUMS_USING_TO_STRING, true);
mapper.configure(org.codehaus.jackson.map.DeserializationConfig.Feature.READ_ENUMS_USING_TO_STRING, true);
mapper.setPropertyNamingStrategy(org.codehaus.jackson.map.PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
mapper.setSerializationInclusion(org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion.NON_EMPTY);
mapper.configure(org.codehaus.jackson.map.DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);

Convert JSON many objects to single JSON using Jackson

I have JSON, with differents levels field, so I want to convert to a single JSON with fields with one level for example:
{
"prop1":"value1",
"prob2":"value2",
"prop3": {
"prop4":"value4",
"prop5":"value5"
}
... many level fields
}
result
{
"prop1":"value1",
"prop2":"value2",
"prop4":"value4",
"prop5":"value5"
.......
}
I'm using Jackson with annotation #JsonProperty("field"), I haven't problem wih fields of first level , but I don´t know how to access field where to into more inside JSON , for this example are prop4 and prop5.
JsonUnwrapped is the annotation to use, it even works for multi-level nesting. For example:
#RunWith(JUnit4.class)
public class Sample {
#Test
public void testName() throws Exception {
SampleClass sample = new SampleClass("value1", "value2", new SubClass("value4", "value5", new SubSubClass("value7")));
new ObjectMapper().writeValue(System.out, sample);
}
#JsonAutoDetect(fieldVisibility=Visibility.ANY)
public static class SampleClass {
private String prop1;
private String prop2;
#JsonUnwrapped
private SubClass prop3;
public SampleClass(String prop1, String prop2, SubClass prop3) {
this.prop1 = prop1;
this.prop2 = prop2;
this.prop3 = prop3;
}
}
#JsonAutoDetect(fieldVisibility=Visibility.ANY)
public static class SubClass {
private String prop4;
private String prop5;
#JsonUnwrapped
private SubSubClass prop6;
public SubClass(String prop4, String prop5, SubSubClass prop6) {
this.prop4 = prop4;
this.prop5 = prop5;
this.prop6 = prop6;
}
}
#JsonAutoDetect(fieldVisibility=Visibility.ANY)
public static class SubSubClass{
private String prop7;
public SubSubClass(String prop7) {
this.prop7 = prop7;
}
}
}
will generate
{"prop1":"value1","prop2":"value2","prop4":"value4","prop5":"value5","prop7":"value7"}
Try implementing the #JsonUnwrapped annotation. More information at http://jackson.codehaus.org/1.9.9/javadoc/org/codehaus/jackson/annotate/JsonUnwrapped.html

Categories

Resources