I have a the class structure as below:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(name = "testA", value = TestA.class),
#JsonSubTypes.Type(name = "testB", value = TestB.class),
#JsonSubTypes.Type(name = "testC", value = TestC.class)
})
public abstract class Test {
}
public class TestA extends Test {
private String firstName;
private String secondName;
private String surName;
}
public class TestB extends Test {
private String adressLine1;
private String adressLine2;
private String adressLine3;
}
public class TestC extends Test {
private String hobby1;
private String hobby2;
private String hobby3;
}
The above classes are serialized into array of json elements, but when I de-serialise them back, i want the structure as below:
public class FlatStructure {
private TestA testA;
private TestB testB;
private TestC testC;
public void setTestA(TestA testA){
this.testA = testA;
}
public TestA getTestA(){
return testA;
}
.....getter and setter for testB and testC...
}
is it possible to convert the array of elements of type testA, testB and testC to properties of FlatStructure class?
You can add a constructor with the annotation #JsonCreator
, in each class Test
and another one in you flat Structure Class,
after that you use an ObjectMapper to create your class FlatStructure
I would recommend to use the annotaions #JsonProperty also on your constructor
check this link
http://buraktas.com/convert-objects-to-from-json-by-jackson-example/
i think that
public class TestA extends Test {
.....
#JsonCreator
public TestA(#JsonProperty("firstName") String name,
#JsonProperty("secondName") String secondeName,
#JsonProperty("surName") String surName){
this.firstName=name;
this.secondeName=secondeName;
this.surName=surName;
}
... getter, setter .....
}
public class TestB extends Test {
.....
#JsonCreator
public TestB(#JsonProperty("adressLine1") String adressLine1,
#JsonProperty("adressLine2") String adressLine2,
#JsonProperty("adressLine3") String adressLine3){
this.adressLine1=adressLine1;
this.adressLine2=adressLine2;
this.adressLine3=adressLine3;
}
... getter, setter .....
}
public class TestC extends Test {
.....
#JsonCreator
public TestC(#JsonProperty("hobby1") String hobby1,
#JsonProperty("hobby2") String hobby2,
#JsonProperty("hobby3") String hobby3){
this.hobby1=hobby1;
this.hobby2=hobby2;
this.hobby3=hobby3;
}
... getter, setter .....
}
public class FlatStructure{
private TestA testA;
private TestB testB;
private TestC testC;
#JsonCreator
public FlatStructure(#JsonProperty("testA") testA testa,
#JsonProperty("testB") testB testb,
#JsonProperty("testC") testC testc){
this.testA =testa;
this.testB =testb;
this.testC =testc;
}
... getter, setter .....
}
Should work properly with a mapper
Edit:
Custom JSON Deserialization with Jackson
http://www.baeldung.com/jackson-deserialization
Related
I have a class like this:
...
#JsonUnwrapped
private DataObject dataObject;
...
Data object is
#Getter
public class DataObject {
private String nested1;
private String nested2;
}
Obviously, this will work if my Json input is
{
"nested1" : "nestedValue1",
"nested2" : "nestedValue2"
}
Is there a way to make it flexible so that it can accept both versions?
But what if I want it to also be able to accept
{
"dataObject: {
"nested1" : "nestedValue1",
"nested2" : "nestedValue2"
}
}
Taking the code of your TestContract class from your previous question below :
public class TestContract {
private String field1;
#JsonUnwrapped
private DataObject dataObject;
}
One way to solve the problem is to write a custom deserializer for your TestContract class that will use the following lombok annotations and the #JsonDeserialize annotation :
#Getter
#Setter
#ToString
#NoArgsConstructor
#JsonDeserialize(using = TestContractDeserializer.class)
public class TestContract {
private String field1;
#JsonUnwrapped
private DataObject dataObject;
}
The custom deserializer will check the field1 property and after will check if the two nested1 and nested2 properties are nested or not inside the dataObject property covering both cases you presented :
public class TestContractDeserializer extends JsonDeserializer<TestContract> {
#Override
public TestContract deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
//read the field1 property
String field1 = node.get("field1").asText();
JsonNode dataObjectNode = node.get("dataObject");
//check if properties are nested or not
if (dataObjectNode != null) {
node = dataObjectNode;
}
String nested1 = node.get("nested1").asText();
String nested2 = node.get("nested2").asText();
DataObject dataObject = new DataObject();
dataObject.setNested1(nested1);
dataObject.setNested2(nested2);
TestContract testContract = new TestContract();
testContract.setDataObject(dataObject);
testContract.setField1(field1);
return testContract;
}
}
I have a class DistributionRule class which contains private Set distributions;
#Getter
#Setter
public class DistributionRule extends BaseModel {
private String ruleName;
private String skuId;
private String catalogueId;
private String categoryId;
private Boolean active;
private RuleLevel level;
private Set<Distribution> distributions;
private Double thresholdValue;
private RuleType ruleType = RuleType.GENERAL;
#Getter
#Setter
public static class Distribution {
private String consumerId;
private Double distribution;
}
}
In RuleServiceImpl, there is a method to get DistributionRule list:
#Override
public List<DistributionRule> getDistributionRules() {
return ruleRepository.findAll();
}
In DivRuleApplicationTests class:
#RunWith(SpringRunner.class)
#SpringBootTest
class DivRuleApplicationTests {
#Autowired
private RuleServiceImpl ruleServiceImpl;
#MockBean
RuleRepository ruleRepository;
#Test
void test() {
assertTrue(true);
}
#Test
public void getDistributionRulesTest(){
Set<Distribution> distributions = new HashSet();
when(ruleRepository.findAll()).thenReturn(Stream
.of(new DistributionRule("rule0010", "0010", " ", " ", true, "SKU", ""),
new DistributionRule("rule0020", "0020", "", "", true, "SKU", ""))
.collect(Collectors.toList()));
assertEquals(2, ruleServiceImpl.getDistributionRules().size());
}
}
How do I pass the "Distribution" values in Stream.of()?
Firstly, the method you are trying to mock on when, returns a List<DistributionRule>. In this case, you don't need to create a Stream.of(new DistributionRule(...)) and then collect it as a List. Simply use List.of(new DistributionRule(...)), as it will return the List you want it.
Secondly, you should annotate both DistributionRule and inner class Distribution with #AllArgsConstructor to generate a constructor with all arguments. This will let you create an DistributionRule object passing the Distribution Set as a parameter:
#Test
public void getDistributionRulesTest(){
Set<Distribution> distributions1 = Set.of(
new Distribution(...), new Distribution(...)
);
Set<Distribution> distributions2 = Set.of(
new Distribution(...), new Distribution(...)
);
when(ruleRepository.findAll()).thenReturn(List.of(
new DistributionRule("rule0010", "0010", ..., distributions1, ..),
new DistributionRule("rule0020", "0020", ..., distributions2, ...)
));
assertEquals(2, ruleServiceImpl.getDistributionRules().size());
}
PS: I usually use the #Data annotation instead of #Getter and #Setter, as it's a shortcut for that and other annotations. You can take a look here if you want to know more: https://projectlombok.org/features/Data
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Flight implements Serializable {
private TrackInformation trackInformation;
private MiscData miscData;
private CargoItems cargoItems;
}
Controller class
#RequestMapping(value = "/getFlightByKey", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public Flight getFlightByKey(#RequestBody #Valid final FlightInfoRequestDTO request)throws NoDataFoundException {
return flightInfoAppService.getFlightByKey(request.getKey());
}
public class TrackInformation extends AbstractRecord {
private static final long serialVersionUID = -5226745652646434627L;
private String sourceFacility;
private ComputerID computerID;
private String sourceTimeStamp;
private Double latitude;
private Double longitude;
private ReportedAltitude reportedAltitude;
private Integer speed;
}
public abstract class AbstractRecord implements Serializable {
}
Now the returned flight object has null fields for nested objects. How can I ignore null fields from nested objects.
did hashCode and equals override in TrackInformation.
You should add #JsonInclude(JsonInclude.Include.NON_NULL) on every nested class as well to ignore nested class null value properties
#JsonInclude(JsonInclude.Include.NON_NULL)
public class TrackInformation extends AbstractRecord {
// properties
}
i'm facing this issue while using Spring JPA and trying to retrieve a List of objects.
This is the class i'm trying to retrieve
#Entity
#Table(name="OBJECTSTERMIC")
public class TermicObject {
#Id
#Column(name="TERMICID")
private long termicId;
#MapsId
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name="OBJECTID",columnDefinition="INTEGER")
private Object object;
#Column(name="CONTECA_RIF")
private int contecaRif;
#Column(name="CONTECA_VAL")
private int contecaVal;
#Column(name="TYPE")
private String type;
//getters and setters
The Object class has the primary key on MySQL stored as an Integer, indeed this is Object
#Entity
public class Object {
#Column(name="OBJECTID")
#Id
#JsonProperty("OBJECTID")
private int objectId;
....
So, nowhere is set a Long...
Now, i simply call in a service class
#Override
public List<TermicObject> findAll() {
return repository.findAll();
}
and got this exception
org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.TypeMismatchException: Provided id of the wrong type for class it.besmart.db_eipo.persistence.model.Object. Expected: class java.lang.Integer, got class java.lang.Long; nested exception is java.lang.IllegalArgumentException: org.hibernate.TypeMismatchException: Provided id of the wrong type for class it.besmart.db_eipo.persistence.model.Object. Expected: class java.lang.Integer, got class java.lang.Long
Where is set that Object Id should be Long?
Have a look at definition of your repository. Does it have right generic type? do you have Integer as second parameter? IMHO this can be root cause. See proposed correct version:
#RepositoryRestResource
public interface TermicObjectRepository extends JpaRepository<TermicObject, Integer> {
public Optional<TermicObject> findById(Integer id);
public List<TermicObject> findAll()
}
As per #Lubo's answer, in my case I was having compatibility issues between String and Long types and as my model required a Long autogenerated id I had to change the repository from
public interface ProductRepository extends JpaRepository<Product, String> {
}
to
public interface ProductRepository extends JpaRepository<Product, Long> {
}
And my controller from
#RequestMapping(path = "/products/delete/{id}", method = RequestMethod.DELETE)
public void deleteProduct(#PathVariable(name = "id") String id) {
productRepository.deleteById(id);
}
to
#RequestMapping(path = "/products/delete/{id}", method = RequestMethod.DELETE)
public void deleteProduct(#PathVariable(name = "id") Long id) {
productRepository.deleteById(id);
}
You have to define your id as a Long datatype.
#Id
#Column(name="TERMICID")
private Long termicId;
also make a change in your repository interface:
public interface ProductRepository extends JpaRepository<Product, Long> {
}
Got this because
public class MyEntity {
#Id()
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private int id; // <-------- int
...
public long getId() { return id; } // <-------- long
}
Not completely sure, but I think this mapping
#Id
#Column(name="TERMICID")
private long termicId;
#MapsId
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name="OBJECTID",columnDefinition="INTEGER")
private Object object;
Makes the id of the Object match the value of termicId which is a long.
use
Long.valueOf(intValue)
to cast int to Long type because you define type Long to #Id
I am trying to make a simple round-trip with a REST API that leads to storing an entity into the db and then returns the stored entity.
Going down works fine and the entity is stored and correctly returned to the REST Controller. However, when I return it, Jackson seems to serialize it incorrectly, as the "name" attribute is not included.
This is the entity:
#Entity
#Configurable
public class MyEntity extends IdentifiableEntity {
private String name;
protected MyEntity() {
};
public MyEntity(String name) {
this.name = name;
}
}
and the extended entity:
#Configurable
#Inheritance(strategy = InheritanceType.JOINED)
#Entity
public abstract class IdentifiableEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Version
#Column(name = "version")
private Integer version = 1;
public String toString() {
return ReflectionToStringBuilder.toString(this,
ToStringStyle.SHORT_PREFIX_STYLE);
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getVersion() {
return this.version;
}
public void setVersion(Integer version) {
this.version = version;
}
}
The REST controller is:
#RestController
#RequestMapping("/service")
public class Service {
#RequestMapping(value = "/public/{name}", method = RequestMethod.GET)
public MyEntity storeEntityPublic(#PathVariable String name) {
System.out.println("Hello " + name
+ ", I am saving on the db. (PUBLIC)");
MyEntity saved = controller.saveEntity(name);
return saved;
}
}
Then my business logic:
#Service
public class LogicController {
#Autowired
private MyEntityRepository myEntityRepository;
public MyEntity saveEntity(String name) {
MyEntity cg = new MyEntity(name);
return myEntityRepository.save(cg);
}
}
I am using Spring repositories:
#Repository
public interface MyEntityRepository extends JpaSpecificationExecutor<MyEntity>,
JpaRepository<MyEntity, Long> {
}
The returned JSON is:
{"id":12,"version":1}
Where is my "name" attribute? Is is set in the variable being returned by the REST controller.
I found the trick: MyEntity needs to have a public get for the property that has to be shown. A good reason to use a DTO pattern.
In response to your "I don't want to have my Entity "dirty"" comment: Jackson allows the use of so-called Mixins. They allow you to define annotations for your class outside the class itself. In your case it could look like this:
public abstract class MyEntityMixin {
#JsonProperty
private String name;
}
You may keep it as a field and annotate the field with #JsonProperty if you like.