jcouchdb: svenson unable to parse json string - java

I'm trying to use jcouchdb (https://code.google.com/p/jcouchdb/) for accessing my CouchDB instance from Java. I have some JSon documents that I'd like to parse into Java classes - with Svenson, used in jcouchdb, and then put those parsed objects into DB. I generate this JSON objects with AVRO (http://avro.apache.org) JSon Encoder, they seem to be ok, but apparently other parsers have problems with them.
My JSon strings look like this:
{
"id":40,
"event_id":"48764322212",
"note":{
"string":"ABC note"
},
"created_date":null,
"event_category":null,
"city":null,
"address":null
}
Which seems valid JSON - validated with http://jsonformatter.curiousconcept.com/
However my Svenson object defined like this:
public class Note {
Long id;
String eventId;
String note;
String createdDate;
String eventCategory;
String city;
String address;
#JSONProperty()
public Long getId() {
#JSONProperty("event_id")
public String getEventId() {
#JSONProperty("note")
public String getNote() {
#JSONProperty("created_date")
public String getCreatedDate() {
#JSONProperty("event_category")
public String getEventCategory() {
#JSONProperty("city")
public String getCity() {
#JSONProperty("address")
public String getAddress() {
}
(setters and getters' bodies intentionally removed)
The error when parsing is:
Cannot set property string on class java.lang.String
It seems that this JSON is parsed correctly (there is a difference in note field):
{
"id":40,
"event_case_id":"000-123123123",
"event_msisdn":"48764322212",
"note":"Planowana data portacji: 2011/01/27 11:42:49",
"created_date":null,
"event_category":null,
"city":null,
"address":null
}
How can I work this out? Perhaps there is another json library that would work for me?

You declare note as a java.lang.String:
public String getNote()
but in the JSON you declare it as an Object with a property named "string":
"note":{
"string":"ABC note"
}
You need to change the JSON or the Bean to match each other. For example, in the second functioning JSON, you declared the JSON note as a string. This is why it works.

Related

Spring Boot parse JSON data to Java Class with different field names

I am new to Spring Boot and I am trying to figure out how to parse json data. I see a lot of tutorials on how to map json string object to an annotated Java class and using and object mapper, like this:
json:
{
"UUID": "xyz",
"name": "some name"
}
public class MyClass{
#JsonProperty
private UUID id;
#JsonProperty
private String name;
#JsonAnyGetter
public UUID getId() {
return this.id;
}
#JsonAnySetter
public void setId(UUID id) {
this.id = id;
}
#JsonAnyGetter
public String getName() {
return this.name;
}
#JsonAnySetter
public void setName(String name) {
this.name = name;
}
}
ObjectMapper objectMapper = new ObjectMapper();
MyClass customer = objectMapper.readValue(jsonString, MyClass.class);
The problem is that the system I am getting the json string from does not match the class naming conventions we use (and I cannot change either one). So, instead of having the example json string above, it might look like this:
{
"randomdstring-fieldId": "xyz",
"anotherrandomstring-name": "some name"
}
This use case only has two fields, but my use case has a larger payload. Is there a way to either map the field names from the json object to the field names in the Java class or is there a way to just parse the json string as a key value pair (so that I can just manually add the fields to my Java object)?
In Jackson with #JsonProperty you can customize the field name with it's annotation parameter value
Therefore, you just have to annotate the entity fields with the #JsonProperty annotation and provide a custom JSON property name, like this:
public class MyClass{
#JsonProperty("original_field_name_in_json")
private UUID id;
...
The #JsonProperty will do it for you:
#JsonProperty("name_in_json")
private Long value;

How to deserialize a complex custom enum in Jackson without hand-rolling a JsonDeserializer?

I have an enum with multiple custom attributes. I'm able to serialize it, and I've created a #JsonCreator method to deserialize it, but it's not working:
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.JsonNode;
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum SomeEnum {
SOME_VALUE("Blah blah blah", "Example description");
private final String displayName;
private final String description;
ScheduleOptimizationRuleCode(String displayName, String description) {
this.displayName = displayName;
this.description = description;
}
public String getCode() { return this.name(); }
public String getDisplayName() {
return displayName;
}
public String getDescription() {
return description;
}
#JsonCreator
public static SomeEnum fromString(#JsonProperty("code") String value) {
return SomeEnum.valueOf(value);
}
}
When serializing, I get the following (correct) output:
{
"code": "SOME_VALUE",
"displayName": "Blah blah blah",
"description": "Example description"
}
When deserializing, my understanding was that Jackson would look in the object representation of my JSON string, dig out the code attribute, and pass in the code as a string to the #JsonCreator annotated method fromString(), but it doesn't work. value is always null.
I've also tried configuring the "mode" on the #JsonCreator annotation (e.g. PROPERTIES, DELEGATING), but it doesn't work. All the examples I've seen only have a single value, so don't have this issue.
I know I can hand-roll a JsonDeserializer and wire it in... and in the Jackson doc, but surely this isn't so complex that I need a hand-made deserializer...
How do I deserialize an enum with multiple attributes?
Finally found an example of deserializing an enum, though only one of the two examples works. From looking at his enum I saw that he was passing in a JsonNode. Worked like a charm!
#JsonCreator
public static SomeEnum fromString(JsonNode json) {
return SomeEnum.valueOf(json.get("code").asText());
}

Using Spring #RequestBody to convert JSON with mixed convention in payload to Entity Object

I am using Spring #RequestBody to map a JSON payload to a Java Object. Unfortunately this JSON payload does not use a set convention but rather has names that use both camelCase and snake_case.
To be clear my Controller looks like this:
#RequestMapping(value="/mobile/device", method = RequestMethod.PUT)
public ResponseEntity<Object> flagDevice (#RequestBody List<MobileDeviceData> deviceInfoList) {
... code here ...
}
with the MobileDeviceData Entity object having several setter methods like:
public void setDeviceName(String deviceName) {
this.deviceName = deviceName;
}
public void setFlagId(int flagId) {
this.flagId = flagId;
}
This works great and without any extra effort when the JSON objects name is camelCase. However for snake_case names I need to add the Annotation:
#JsonProperty("flag_id")
private int flagId;
in order for it to be picked up.
I know it's not a good idea to use the #JsonProperty if it can be avoided as you then will need to annotate every parameter. My question is, is there a more general way to enforce matching snake_case with the corresponding camelCase in the Entity object? And obviously to do it without screwing up the ones that are already camelCase.
As per the article here, there is a simple approach to deserialize the MobileDeviceData class. Here is the sample code as below:
#JsonDeserialize(using = UserDeserializer.class)
public class User {
private ObjectId id;
private String username;
private String password;
public User(ObjectId id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public ObjectId getId() { return id; }
public String getUsername() { return username; }
public String getPassword() { return password; }
}
Assume User is the class we’re interested in writing the Deserializer for. Not much is notable here, except for the annotations that tell Jackson who knows how deserialize this class.
public class UserDeserializer extends JsonDeserializer {
#Override
public User deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext) throws IOException {
ObjectCodec oc = jsonParser.getCodec();
JsonNode node = oc.readTree(jsonParser);
return new User(null,
node.get("username").getTextValue(),
node.get("password").getTextValue());
}
}
The deserializer is created by extending Jackson’s abstract JsonDeserializer class, and giving it the type we want to deserialize to. Difficult is figuring out that you can reference the JSON by field name with the JsonParser's ObjectCodec.
I hope it helps.
Please feel free to comment if needed!
Having been working on this a bit, I now realize doing anything like what was requested would be counterproductive.
When you receive (deserialize) a JSON Object, it is generally expected that you will deliver (serialize) with the same parameters. If an implementation extracted both camelCase and underscore parameters the same way, then it would not know how to deserialize correctly later on. By following a standard convention and then using #JsonProperty for all the exceptions, it remains possible to deserialize and later deliver the JSON object just as it was received.

How does the Jackson mapper know what field in each Json object to assign to a class object?

Let's say I have a Json object like this:
{
"name": "Bob Dole",
"company": "Bob Dole Industries",
"phone": {
"work": "123-456-7890",
"home": "234-567-8901",
"mobile": "345-678-9012"
}
}
And to help me read it, I use Jackson's Object Mapper with the following class:
public class Contact {
public static class Phone {
private String work;
private String home;
private String mobile;
public String getWork() { return work; }
public String getHome() { return home; }
public String getMobile() { return mobile; }
public void setWork(String s) { work = s; }
public void setHome(String s) { home = s; }
public void setMobile(String s) { mobile = s; }
}
private String name;
private String company;
private Phone phone;
public String getName() { return name; }
public String getCompany() { return company; }
public Phone getPhone() { return phone; }
public void setName(String s) { name = s; }
public void setCompany(String s) { company = s; }
public void setPhone(Phone p) { phone = p; }
}
My question is, how (using the simplest explanation possible), does the Object mapper "deserialize" the Json object? I thought it was matching variable names, but changing them by a few letters didn't affect the output. Then, I tried switching the order of the set() functions, but that didn't do anything. I also tried both, but that was also useless. I'm guessing there's something more sophisticated at work here, but what?
I tried to look in the documentation and past code, but I didn't see an explanation that made sense to me.
Without Annotations:
Without any annotations, it does what is called POJO mapping, it just uses reflection on the instance members and uses some rules about how to map the keys in the json to the names of the instance members. *note: it works on private members as well as public or package protected as well
If it doesn't match the names of the instance members, then it starts trying to match the getXXX and setXXX methods, if it doesn't match anything then it gives up.
With Annotations:
It uses the metadata supplied by the annotations to do the mapping and conversions.
It is always better to explicitly use the annotations when you have the source to add them to, then there is no guess work on what gets mapped to what.
Remember explicit is always better than implicit!
This is all well documented on the WIKI:
Mapping and Annotations
JSON Schema:
I am creating JSON Schema definitions for all my new projects now to document what is and isn't valid JSON according to the schema rules engine. It is a great way to document your data structures and eliminate parsing errors.

How to Dynamically generate and retrieve JSON in Java code?

I have a use case where we have to send different JSONs to different servers.
The difference is only between JSON keys, the meaning the keys carry is same and so is the data.
For example server XYZ wants JSON data to be sent in this format:
{ "firstName":"Sam", "lastName":"Jones"}
Now server ABC wants JSON data to be sent in this format:
{ "fName":"Sam", "lName":"Jones"}
And firstName and lastName data is populated via a POJO.
So, How do I achieve this? I do not want to clutter the code with if-else conditions.
But wnat to have something which would work like a template loaded dynamically and create the JSON data and also retrieve it back to the POJO.
You should create two POJOs. One for each server. Each POJO can have different property names to satisfy each of the server's requirements.
Or the POJOs can have the same property names, but be annotated to generate different JSON properties. A JSON library like Jackson can do this using the JsonProperty annotation.
How about this strategy?
1. Defines the interface to be used as a common..
interface People{
public String getRegularFirstName();
public String getRegularLastName();
}
2. Define each POJO with implemented interface
//class for "{ "firstName":"Sam", "lastName":"Jones"}"
class PeopleData2 implements People{
private String firstName;
private String lastName;
public String getRegularFirstName(){
return firstName;
}
public String getRegularLastName(){
return lastName;
}
//getter setter here..
}
//class for "{ "fName":"Sam", "lName":"Jones"}"
class PeopleData1 implements People{
private String fName;
private String lName;
public String getRegularFirstName(){
return fName;
}
public String getRegularLastName(){
return lName;
}
//getter setter here..
}
3. Make each json format deserved each POJO classes..
It is not dinamically strategy because it need to add class whe new format comes up.
but it will help system scalability

Categories