I have the below json, where the body key contains a value which is a string representation of a JSON object, how do I convert it to a Java Object ?
I can extract the body value by converting the JSON to a Map, but I don't know how I should proceed from there
input.json file
{
"body": "{\n\t\"username\": \"TestUser\",\n\t\"password\": \"TestPassword\"\n}"
}
The User POJO is as below,
class User {
private String username;
private String password;
... getters, setters and no-arg constructor
}
My code looks something like this, I need to implement convertToUser function
public static void main(String[] args) {
String jsonContent = readJsonFile("input.json");
String escapedJsonBody = getBody(s);
User user = convertToUser(escapedJsonBody, User.class);
}
I am already using jackson java library, any insights on doing this with jackson is highly appreciated.
One way to do it is to create DTOs and converter. Having DTOs like (i have nested the class declarations jsut to save space in answer):
#Getter #Setter
public class Input { // this level maps to the whole input.json
#JsonDeserialize(using = BodyDeserializer.class) // custom deserializer below
private Body body; // this is the body-attribute in JSON
#Getter #Setter
public static class Body {
private User user;
#Getter #Setter
public static class User {
private String username;
private String password;
}
}
}
the converter:
public class BodyDeserializer extends JsonDeserializer<Body> {
private ObjectMapper om = new ObjectMapper(); // this is to read the user from string
#Override
public Body deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String embedded = p.readValueAs(String.class);
Body body = new Body();
body.setUser(om.readValue(embedded, User.class)); // here is the trick
return body;
}
}
Use like:
ObjectMapper om = new ObjectMapper();
String input = "{\"body\": \"{\\n\\t\\\"username\\\": \\\"TestUser\\\",\\n\\t\\\"password\\\": \\\"TestPassword\\\"\\n}\"}";
Input r = om.readValue(input, Input.class);
This way the conversion happens in generic way only con might be that you do not like to create DTOs and dig the user like Input.getBody().getUser();
To convert a JSON String to a java pojo you can use Jackson's ObjectMapper class that will assist you to do this.
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.readValue(inputJson, User.class);
More info can be found on Jackson's github page
Related
I get a JSON string that I convert to an object. One of the property within the JSON sometimes would be null. If the property is null, I want the default value to be set to 0.
This is my class:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
public class Car {
#JsonProperty(value = "car_name")
private String carName;
#JsonProperty(value = "car_value")
private Long carValue;
#JsonProperty(value = "Specifications")
private Map<String, String> Specifications;
}
I use object mapper to convert the JSON string to the object
public List<Car> stringToCar(String json) throws JsonProcessingException {
ObjectMapper om = new ObjectMapper();
return om.readValue(json, new TypeReference<List<Car>>() {} );
}
carValue would sometimes have null value, if that happens I want it be set as 0. Is it possible to do in a efficient way rather than looping through the object and manually setting the value to 0
You have multiple ways to do this.
1) Setter that receives long instead
This is actually not straightforward but it works. If you define the setter as follows it will do what you need:
public void setCarValue(long carValue) {
this.carValue = carValue;
}
However, this feels like a hack to me, so I would not suggest you use it.
2) Custom deserializer
This one is more complex but also much easier to understand and explicit about your intent.
public class CustomLongDeserializer extends JsonDeserializer<Long> {
#Override
public Long deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
String text = jp.getText();
if (text == null || text.isEmpty()) {
return 0;
} else {
return Long.valueOf(text);
}
}
}
Then you could apply the serializer on the attribute as follows:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
public class Car {
#JsonProperty(value = "car_name")
private String carName;
#JsonProperty(value = "car_value")
#JsonDeserialize(using = CustomLongDeserializer.class)
private Long carValue;
#JsonProperty(value = "Specifications")
private Map<String, String> Specifications;
}
Or apply it as a global deserializer to be used to deserialize every single Long:
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Long.class, new CustomLongDeserializer());
mapper.registerModule(module);
It's not a generic solution, but in your specific use case you could use the long primitive instead of Long which will coerce null to 0 when deserialized.
If you don't want to create a separate class like #João Dias mentioned, you can define the constructor yourself instead of using #AllArgsConstructor, something like :
public Car(String carName,
Long carValue,
Map<String, String> Specifications){
this.carName = carName;
this.Specifications = Specifications;
this.carValue = (carValue == null ? 0 : carValue);
}
The same can be used for #Builder annotation like :
builder().carValue(value == null ? 0 : value).build();
Again, if you have more variables to handle you should consider previous response.
Having 2 simple classes like:
#Setter
#Getter
public class Continent {
private String id;
private String code;
private String name;
}
#Setter
#Getter
public class Country {
private String id;
private String alpha2Code;
private String alpha3Code;
private String name;
private Continent continent;
}
when reading the following yaml:
id: brazil
alpha2_code: BR
alpha3_code: BRA
name: Brazil
continent_id: south-america
I would like to use the continent_id to retrieve the Continent from a application scoped List<Continent>.
The best thing I could think of is using a custom Deserializer like:
public class CountryDeserializer extends StdDeserializer<Country> {
public CountryDeserializer() {
super(Country.class);
}
#Override
public Country deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
// This works... the `continentId` is retrieved!
JsonNode node = jp.getCodec().readTree(jp);
String continentId = node.get("continent_id").asText();
// How to access application scoped continents? Use injectable value?
Continent continent = getContinent(continentId);
// Read value for other properties; don't want to read other properties manually!
Country country = jp.getCodec().readValue(jp, Country.class);
// But unfortunately this throws a StackOverflow...
country.setContinent(continent);
return country;
}
}
But the problem is I would like Jackson to automatically read the other properties.
I don't want to this manually as if in the future a property is added it might be forgotten, and with other entities with 20 properties this becomes very cumbersome...
I tried with Country country = jp.getCodec().readValue(jp, Country.class); but that gives stack overflow exception as it gets in a loop with the custom deserializer obviously.
Is there a way to solve this using Jackson, or is there another better approach to get and set the Continent in this scenario?
Note I'm working with a pre-defined set of domain classes I cannot change.
I can modify the object mapper and add mixins if needed.
Instead of using a CountryDeserializer I've implemented it using a ContinentReferenceDeserializer.
This way the other Country properties are deserialized "automatically".
It looks like:
public class ContinentReferenceDeserializer extends StdDeserializer<Continent> {
public ContinentReferenceDeserializer() {
super(Continent.class);
}
#Override
public Continent deserialize(JsonParser parser, DeserializationContext context) throws IOException {
String id = parser.getText(); // returns the continent id (`continent_id` in json)
Map<String, Continent> continents = (Map<String, Continent>) context.findInjectableValue("continents", null, null);
return continents.gett(id);
}
}
and it is used in the CountryMixIn like:
public abstract class CountryMixIn {
#JsonProperty("continent_id")
#JsonDeserialize(using = ContinentReferenceDeserializer.class)
abstract Continent getContinent();
}
Note that if you don't use Mix-ins but directly annotate domain/dtoa classes, above can be applied to these as well instead.
The ObjectMapper can be setup then like:
Map<String, Continent> continents = .. // get the continents
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(Country.class, CountryMixIn.class);
mapper.setInjectableValues(new InjectableValues.Std().addValue("continents", continents));
and then can be called like:
String json = .. // get the json
Country country = mapper.readValue(json, Country.class);
Heyy,
I have a JSON String i.e.
{"userId":"WaNenOnQt","photos":[{"photo_url":"vendor_photos/WaNenOnQt/web_(138)(4thcopy).JPG","index":1},{"photo_url":"vendor_photos/WaNenOnQt/54230451_265006064447640_7942942433146217157_n.jpg","index":2}]}
I want only the List data i.e. -
[{"photo_url":"vendor_photos/WaNenOnQt/web_(138)(4thcopy).JPG","index":1},{"photo_url":"vendor_photos/WaNenOnQt/54230451_265006064447640_7942942433146217157_n.jpg","index":2}]
Is there any replace function in java or how can i segregate the list ?
assuming you are getting that JSON through an endpoint you are exposing, you should bind it to the method signature, for example if you are using Spring:
public class MyPojo {
private String userId;
private List<Photo> photoList;
//getters & setters
}
Photo class
public class Photo {
#JsonProperty("photo_url")
private String url;
private int index;
//getters & setters
}
Controller class
#RequestMapping(value = "/test", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> test(#RequestBody MyPojo request) {
List<Photo> photos = request.getPhotoList();
}
Alternatively if you don't have an endpoint, you can manually transform the JSON String to a POJO and vice-versa, using Java's ObjectMapper. For example:
public void transform(String jsonString) throws... {
ObjectMapper mapper = new ObjectMapper();
MyPojo pojo = mapper.readValue(jsonString, MyPojo.class);
List<Photo> photo = pojo.getPhotoList();
}
I'm also assuming that you need a Java List, and don't need the JSON array simply as a String.
You van use small json library
String jsonstring = "{\"userId\":\"WaNenOnQt\",\"photos\":[{\"photo_url\":\"vendor_photos/WaNenOnQt/web_(138)(4thcopy).JPG\",\"index\":1},{\"photo_url\":\"vendor_photos/WaNenOnQt/54230451_265006064447640_7942942433146217157_n.jpg\",\"index\":2}]}";
JsonValue json = JsonParser.parse(jsonstring);
JsonValue photos = json.asObject().first("photos");
String result = photos.toCompactString();
I have two classes Athlete and Injury, the last one contains Athlete object, when the serialization happens I get the following JSON representation back:
{"id":X,"kindOfInjury":"...","muscle":"...","side":"...","outOfTrainig":Y,"injuryDate":"2018-Jun-02","athlete":{"id":X,"firstName":"...","lastName":"...","age":X,"email":"..."}}
I don't want to get all the information about Athlete - just an id value, like "athleteId":1, instead of getting the entire object representation.
So, I have found that I need to apply my custom Serializer which implements StdSerializer on Injury class. So this is what I got so far:
class InjurySerializer extends StdSerializer<Injury> {
public InjurySerializer() {
this(null);
}
public InjurySerializer(Class<Injury> i) {
super(i);
}
#Override
public void serialize(
Injury value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeNumberField("id", value.getId());
jgen.writeStringField("kindOfInjury", value.getKindOfInjury());
jgen.writeStringField("muscle", value.getMuscle());
jgen.writeStringField("side", value.getSide());
jgen.writeNumberField("outOfTraining", value.getOutOfTraining());
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MMM-dd");
Date date = new Date();
String ourformat = formatter.format(date.getTime());
jgen.writeStringField("injuryDate", ourformat);
jgen.writeNumberField("athleteId", value.getAthlete().getId());
jgen.writeEndObject();
}
}
And the actual Injury class:
#Entity
#Table(name = "INJURY")
#JsonSerialize(using = InjurySerializer.class)
public class Injury {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "INJURY_ID")
private Long id;
#Column(name = "KIND_OF_INJURY")
private String kindOfInjury;
#Column(name = "MUSCLE")
private String muscle;
#Column(name = "SIDE")
private String side;
#Column(name = "OUT_OF_TRAINING")
private Integer outOfTraining;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MMM-dd")
#Column(name = "INJURY_DATE")
private Date injuryDate;
#ManyToOne
#JoinColumn(name = "ATHLETE_ID")
private Athlete athlete;
So, this solution works, but it looks terrible...
Question is the following:
1) Is there any mechanism which provides me functionality to change the serialization of only ONE property which I really need, instead of writing all this tedious code, where the actual change is only in this line? :
jgen.writeNumberField("athleteId", value.getAthlete().getId());
2) Could you recommend me something to read about Jackson because at this point I have a little bit mess in my head about it?
Thanks for the patience and I'm looking forwards for your responses :)
You can use the Data Transfer Object (DTO) for that purposes.
Create a simple POJO like this:
public class InjuryDTO {
//all other required fields from Injury model...
#JsonProperty("athlete_id")
private Long athleteId;
}
And converter for it:
#Component
public class InjuryToDTOConverter{
public InjuryDTO convert(Injury source){
InjuryDTO target = new InjuryDTO();
BeanUtils.copyProperties(source, target); //it will copy fields with the same names
target.setAthleteId(source.getAthlete().getId());
return target;
}
}
You can use it like that:
#RestController("/injuries")
public class InjuryController {
#Autowired
private InjuryToDTOConverter converter;
#Autowired
private InjuryService injuryService;
#GetMapping
public InjuryDTO getInjury(){
Injury injury = injuryService.getInjury();
return converter.convert(injury);
}
}
The benefit of this approach is that you can have multiple DTOs for different purposes.
You might find it less tedious to use the #JsonIgnore annotation instead of writing a custom serializer. Take this example
public class Person {
private int id;
#JsonIgnore
private String first;
#JsonIgnore
private String last;
#JsonIgnore
private int age;
// getters and setters omitted
}
When Jackson serializes this class, it only includes the "id" property in the resulting JSON.
#Test
void serialize_only_includes_id() throws JsonProcessingException {
final var person = new Person();
person.setId(1);
person.setFirst("John");
person.setLast("Smith");
person.setAge(22);
final var mapper = new ObjectMapper();
final var json = mapper.writeValueAsString(person);
assertEquals("{\"id\":1}", json);
}
You can try manupulating json string using basic string replace method.
I ran your json and converted it to your desired format:
public static void main(String args[]) {
String json = "{\"id\":123,\"kindOfInjury\":\"...\",\"muscle\":\"...\",\"side\":\"...\",\"outOfTrainig\":Y,\"injuryDate\":\"2018-Jun-02\",\"athlete\":{\"id\":456,\"firstName\":\"...\",\"lastName\":\"...\",\"age\":14,\"email\":\"...\"}}";
JsonObject injury = new JsonParser().parse(json).getAsJsonObject();
JsonObject athelete = new JsonParser().parse(injury.get("athlete").toString()).getAsJsonObject();
String updateJson = injury.toString().replace(injury.get("athlete").toString(), athelete.get("id").toString());
updateJson = updateJson.replace("athlete", "athleteId");
System.out.println(updateJson);
}
output:
{"id":123,"kindOfInjury":"...","muscle":"...","side":"...","outOfTrainig":"Y","injuryDate":"2018-Jun-02","athleteId":456}
Dependency:
implementation 'com.google.code.gson:gson:2.8.5'
If you can replace with regex that will be bit more cleaner.
I am trying to implement a university project where I try to fetch values from two Json fields and map it to one pojo class.
Sample Json:
"event":[{"D17-32":0,"S10":"D"}]
Pojo class
public class Event {
#JsonDeserialize(using = SignalCustomDeserializer.class)
#JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
#JsonProperty("S10")
private Signal S10;
#JsonProperty("D17-32")
private String D17_32;
//Getter and setter implementation
}
Class which I need to serialize the fields to
public class Signal{
private String value;
private String detectorId;
private int detectorValue; //this value has to be fetched from another json
//Getter and setter implementation
}
Custom deserializer class
public class SignalCustomDeserializer extends JsonDeserializer {
#Override
public Signal deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
String signalId = jsonParser.getCurrentName();
String signalVal = jsonParser.getValueAsString();
String detectorVal = jsonParser.getValueAsString("D01-16");
Signal signal = new Signal();
signal.setValue(signalVal);
signal.setDetectorId(getDetectorId(signalId));
return signal;
}
}
I am able to get the signalId and signalValue but I am unable to get the value for the other field. I am unsure if its available in the JsonObject when the custom deserialization class is called.