I'd like to convert a Java POJO into a Map without using JSON serialization for the properties (e.g. that Date's are converted to a long or a ISO8601 String). I just want the fields to be retained as they are.
For example if I have a POJO defined like this:
public class MyPojo {
private Date date;
private String x;
public MyPojo(Date date, String x) {
this.date = date;
this.x = x;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getX() {
return x;
}
public void setX(String x) {
this.x = x;
}
}
I know I can use Jackson as described in this Stackoverflow question but the result is not what I want. For example if I do:
Map<String, Object> x = new ObjectMapper().convertValue(new MyPojo(new Date(), "x"), new TypeReference<Map<String, Object>>() {});
System.out.println(x);
I get this result:
{date=1528521584984, x=x}
whereas I would simply like a Map retaining the java.util.Date instance without being serialized.
Note that I obviously would like this work for nested Pojo's such as:
public class MyPojo {
private MyOtherPojo otherPojo;
...
}
How can I achieve this?
First off, going from a POJO to a Map seems like a bad idea. But I guess that you have reasons for wanting to do it ...
The simple approach is to just create a HashMap and populate it using a sequence of put calls. That gives you a map that contains the same information as the original POJO, but is not "connected" to is.
If you want the Map to be a view of the original POJO, there are 3rd-party libraries that can be used to do this. For example, org.apache.commons.beanutils has classes for wrapping an POJO that follows the JavaBeans conventions as a DynaBean. You can then adapt that as a Map using DynaBeanMapDecorator.
Mapping nested POJOs to nested maps would require more work. (And it contradicts your requirement that you can get the value of getData() as a Date POJO rather than as a Map!)
Jackson will serialize the Date to a timestamp format by default (number of milliseconds since January 1st, 1970, UTC). #JsonFormat annotation can be used to control the date format on individual classes.
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh:mm:ss")
private Date date;
Related
This question already has answers here:
Jackson ObjectMapper returns null when Object variable is set to Private
(4 answers)
Closed 28 days ago.
My data is as below and saved in test folder with name risk.json
[{
"Risk": "BN",
"Classification": null,
"LastDefaultDate": "1915-04-14 00:00:00"
}]
I have RiskClass defined as below
#Data
#JsonIgnoreProperties({"document"})
public class RiskClass implements KeyedObject {
String risk;
String classification;
Date lastDefaultDate;
#Override
public String getKey() {
return risk;
}
}
In my data prepare class i am trying to populate one of the map by doing below
List<RiskClass> rList = ObjectUtils.jsonStringToObjList(readFileAsString("test", "risk.json"), RiskClass.class);
Map<String, RiskClass> riskMapLocal = new HashMap<>();
for (RiskClass rMap : rList) {
riskMapLocal.put(rMap.getRisk(), rMap);
}
now when i try to print riskMapLocal, under lastDefaultDate i get null value.
Property names in json start with uppercase - Risk, etc. Fields in POJO start with lowercase - risk, so they can't be matched automatically. You can either:
Change them in json to be lowercase
Use annotation JsonProperty on the fields to specify the name of the property to match for this field.
public class RiskClass {
#JsonProperty("Risk")
String risk;
}
Add an annotation over your property lastDefaultDate:
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
LocalDateTime lastDefaultDate;
Also change type Date to LocalDateTime as Date type is outdated (pun intended). For more details look at this question and its best answer: Spring Data JPA - ZonedDateTime format for json serialization
Similar to this question. I have an interface DateRangeModel:
I use this to automatically validate dates in implementers:
public interface DateRangeModel {
#ApiModelProperty(value = "From date. Must be less than to date.")
#Column()
Date getFromDate();
void setFromDate(Date date);
#ApiModelProperty(value = "To date. Must be greater than to date.")
#Column()
Date getToDate();
void setToDate(Date date);
/**
* Checks that if both dates are populated, a valid date range is used.
*
* #return true if the date is a valid range.
*/
#AssertTrue(message = "To date must be greater than or equal to from date.")
#JsonIgnore
default boolean areDatesValid() {
if (getToDate() != null && getFromDate() != null) {
return !getFromDate().after(getToDate());
}
return true;
}
}
I implement it like this:
#EqualsAndHashCode
#Data
#Builder
public class BirthdayParty implements DateRangeModel {
Date fromDate;
Date toDate;
String name;
}
Which compiles and seems to work, but I get that error when running PMD:
Returning a reference to a mutable object value stored in one of the object's fields exposes the internal representation of the object.
How can I either accomplish what I want (an interface with to/from date validation) without having to implement the setDate methods in all implementers (which I think would defeat the purpose)?
The problem is that java.util.Date is mutable and you return it in your getters. So someone could do this:
BirthdayParty party = ...;
Date fromDate = party.getFromDate();
...
Date someDate = fromDate;
...
// you might not be aware that this also changes the fromDate in party
someDate.setTime(12345678);
There are four things you can do:
Disable the PMD rule.
Suppress the warning everywhere you use one of these a classes.
Don't use Lombok and copy the dates in your setters and getters instead of just storing/returning the reference to a date.
Use java.time.ZonedDateTime (or LocalDateTime) instead of Date. ZonedDateTime is immutable and should not lead to this warning.
I suggest the fourth option. Not only because it gets rid of the PMD warning but also because the new time API in Java 8 is so much better and easier to use than java.util.Date.
I have a rest service which validates date now i need to modify it to take two dates, but i don't know if to use #PathParam or #QueryParam and if i can insert it between two #PathParam
This it the original code :
#Path("isDateValid/{date}/{itemId}")
public boolean isDateValid(#PathParam("date") Date date, #PathParam("itemId") Long itemId) {
Should i do like this :
#Path("isDateValid/{startDate}/{endDate}/{itemId}")
public boolean isDateValid(#PathParam("startDate") Date startDate, #PathParam("endDate") Date endDate, #PathParam("itemId") Long itemId) {
If you do not want to use third party stuff, I suggest you define a format for the text-date. You can use the SimpleDateFormat class (avoid the space). The you can use the following code.
#Path("isDateValid/{itemId}")
public boolean isDateValid(#PathParam("itemId") Long itemId) {
#QueryParam("begin") String sBegin;
#QueryParam("end") String sEnd;
SimpleDateFormat sdf = new SimpleDateFormat(/* Your patern, for example "yyMMddHHmmssZ"*/);
Date dBegin = sdf.parse(sBegin);
Date dEnd = sdf.parse(sEnd);
/*
...
*/
}
Date Class is cannot serialize using JAX-RS as it is not a simple type. You need to develop the same using MessageBodyReader/Writer .
Click Here for more
Or you can use some third party stuff to configure to get it done.
Click Here for more
We have a case, where we require only the day and the month and thus would use the java.time.MonthDay (Javadocs) to represent that information.
We are aware that we could create our own JPA object for persistence or just use the java.sql.Date object, but that generally requires an unrequired year information.
Another way is to call the method .atYear(int) (Javadoc) (with a fictitious year) on it and receive a java.time.LocalDate (Javadoc), which can be easily converted to java.sql.Date. But this is prone to missunderstandings in the future (and also persist the year information).
Is there some "elegant"/supposed solution for this case? Or is there a replacement for SQL that supports the whole new date and time API for Persistence.
Another case would be java.time.YearMonth (Javadoc).
Thanks for reading!
Since SQL databases don't have a type compatible with MonthDay, use a VARCHAR columns, and simply use toString() and MonthDay.parse().
Or use a custom DateTimeFormatter, if you don't like the --12-03 format.
The default format will correctly sort, as a string.
here are the code snippets:
// column define:
#Column(name = "date", columnDefinition = "mediumint", nullable = false)
#Convert(converter = MonthDayIntegerAttributeConverter.class)
protected MonthDay date;
// converter define:
public class MonthDayIntegerAttributeConverter implements AttributeConverter<MonthDay, Integer> {
#Override
public Integer convertToDatabaseColumn(MonthDay attribute) {
return (attribute.getMonthValue() * 100) + attribute.getDayOfMonth();
}
#Override
public MonthDay convertToEntityAttribute(Integer dbData) {
int month = dbData / 100;
int day = dbData % 100;
return MonthDay.of(month, day);
}
}
How can I persist a jodatime YearMonth object to a sql database?
#Entity
public class MyEntity {
private YearMonth date; //how to persist?
//some more properties
}
I want later to be able to pick all entities by a specific month.
The better question is how do you intend to use the data in the database? Generally you want to store dates in databases using data types supported by the target database as opposed to strings or composite integers. That would allow you to use the built in database date/time functions and validations as opposed to needing to implement that logic in your application.
You can certainly add methods to your entity to convert to/from jodatime values to your entity for the parts of your application that need it.
You need a javax.persistence.Converter for this (see How to convert java.util.Date to Java8 java.time.YearMonth):
#Entity
public class MyEntity {
#Convert(converter = YearMonthConverter.class)
private YearMonth date; //how to persist?
//some more properties
}
The following is an example converter for Java8 java.time.YearMonth to java.utilDate. You need to change java.time.YearMonth to org.joda.time.YearMonth and use the appropriate methods to convert to/from the desired type:
(imports omitted):
#Converter(autoApply = true)
public class YearMonthConverter implements AttributeConverter<YearMonth, Date> {
#Override
public Date convertToDatabaseColumn(YearMonth attribute) {
// uses default zone since in the end only dates are needed
return attribute == null ? null : Date.from(attribute.atDay(1).atStartOfDay(ZoneId.systemDefault()).toInstant());
}
#Override
public YearMonth convertToEntityAttribute(Date dbData) {
// TODO: check if Date -> YearMonth can't be done in a better way
if (dbData == null) return null;
Calendar calendar = Calendar.getInstance();
calendar.setTime(dbData);
return YearMonth.of(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1);
}
}