I am trying to understand Jackson and have to ask you one thing about it.
In the example below I have two classes. One of them - object to be converted to JSON, another - main method.
I know that Jackson needs default constructor but decided to experiment. I have two constructors.
So the question is - why is my output {"name":"Lion"} and int value can not be seen at all?
public class Lion extends AbstractCat {
int age;
public String name;
public Lion(int age, String name) {
this.age = age;
this.name = name;
}
public Lion() {};
}
public class Var {
public static void main(String[] args) throws IOException, JAXBException {
Lion lion = new Lion(32,"Lion");
System.out.println(converToJSON(lion));
}
public static String converToJSON(Object o) {
ObjectMapper objectMapper = new ObjectMapper();
StringWriter stringWriter = new StringWriter();
try {
objectMapper.writeValue(stringWriter,o);
} catch (IOException e) {
e.printStackTrace();
}
return stringWriter.toString();
}
}
Jackson ignores age, because it is private. Either you make age public or add a public getter method (i.e. public int getAge() { return age; }). The latter is usually the prefered variant as public fields are exposing your classes internals.
Related
I'm working on a project that requires me to serialize and deserialize generic objects. The way I'm going about this, is defining an abstract class Serializer that implements a toBytes() and a static fromBytes(). All is well with this approach, as I can pass an object instance to a generic class Foo that expects a Serializer subclass, and I can ensure the object knows how to serialize and deserialize itself.
Now my question. Java serialization kinda sucks. I have multiple implementations I'd like to try swapping in and out, and ultimately I'd like the user to be able to decide the format. How would I go about changing the implementation details of Serializer? I know I can't override static methods, so how would I do this without decoupling Foo and Serializer and not being able to ensure my generic object has the appropriate toBytes() and fromBytes() method in Foo?
Here is code if anyone is confused:
public abstract class Serializer {
public static Serializer fromBytes(byte[] bytes) {
...
}
public byte[] toBytes() {
...
}
}
public class Foo<T extends Serializer> {
private T t;
public Foo(T t) {
this.t = t;
}
public void foo() {
t.toBytes(); //this will polymorph into the correct call because it's called on the object instance and not the Serializer class
}
public void bar(byte[] bytes) {
T.fromBytes(bytes); // I'd like to be able to override this method so I can use different implementations
}
}
I'm not sure if this is a good approach, but how about using Jackson library and serialize your object as a json node? for example:
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
#Type(value = SoundFile.class, name = "sound"),
#Type(value = VideoFile.class, name = "video")
})
abstract class File{
private String id;
private String type;
#JsonCreator
public File(#JsonProperty("id") String id)
{
this.id=id;
}
public String getId() {return this.id;}
public abstract String getType();
}
class SoundFile extends File{
#JsonCreator
public SoundFile(#JsonProperty("id") String id) {
super(id);
}
#Override
public String getType() {
return "sound";
}
}
class VideoFile extends File{
#JsonCreator
public VideoFile(#JsonProperty("id") String id) {
super(id);
}
#Override
public String getType() {
return "video";
}
}
public class GenericApp {
public static void main(String[] args) {
ObjectMapper om = new ObjectMapper();
List<File> files = Arrays.asList(new VideoFile("1"),new SoundFile("2"));
//serialize
List<byte[]> fileSerialized = files.stream().map(file->{
try {
return om.writeValueAsBytes(file);
}catch(Exception e) {return null;}
}).collect(Collectors.toList());
//de-serialize
List<File> filesDeSerialized = fileSerialized.stream().map(bytes ->{
try {
return om.readValue(bytes, File.class);
}
catch(Exception e) {return null;}
}).collect(Collectors.toList());
filesDeSerialized.stream().forEach(file->{
System.out.println("id :"+file.getId()+" - "+file.getClass());
});
}
}
this would properly deserialize these objects and print:
id :1 - class com.dsncode.stackoverflow.VideoFile
id :2 - class com.dsncode.stackoverflow.SoundFile
however, you should define a #JsonTypeInfo and a #JsonSubType for all your sub-classes of your Generic Type. Because, by indicating this field, you will indicate to Jackson deserializer, which class should create for your generic type.
Here is my Code :
public class SearchByLambda {
private Map<String,Consumer<Person>> searchCritertiaHolder = new HashMap<String,Consumer<Person>>();
private static final String AGED = "aged";
public SearchByLambda(){
searchCritertiaHolder.put(AGED, (Person p)-> {p.filterAgedPerson(p);} );
}
private Consumer<Person> getFilter(String personType){
return searchCritertiaHolder.get(personType);
}
public static void main(String[] args) {
SearchByLambda searchUsage = new SearchByLambda();
Person p = new Person(59,"shailesh");
Person p1 = new Person(58,"ganesh");
searchUsage.getFilter(AGED).accept(p);
searchUsage.getFilter(AGED).accept(p1);
Person.printAgedPersons();
}
}
class Person{
private static List<Person> agedPersons = new ArrayList<>();
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(int age,String name){
this.age = age;
this.name = name;
}
public void filterAgedPerson(Person person){
if(person.getAge() > 58){
agedPersons.add(person);
}
}
public static void printAgedPersons(){
for(Person person : agedPersons){
System.out.println(person.getName());
}
}
}
When I replace following Lambda expression
searchCritertiaHolder.put(AGED, (Person p)-> {p.filterAgedPerson(p);});
with
searchCritertiaHolder.put(AGED, Person::filterAgedPerson);
it gives me compilation error. I am using java 8 and and compiling through eclipse. Why is this so? Why cannot I assign method reference for instance method of any arbitrary object to consumer functional interface?
Your definition of filterAgedPerson takes a Person as an argument, even though it is not a static method. It doesn't need to, and it shouldn't if you want to use it as a Consumer<Person>. What you are ending up with is something compatible with BiConsumer<Person, Person>.
It might help to think of it this way: method references to non-static methods always take an "extra" argument which is used as this.
The easiest way for you to fix this with your current code structure is to modify the filterAgedPerson method to not take a Person as an argument
public void filterAgedPerson() {
if (this.getAge() > 58) {
agedPersons.add(person);
}
}
As an aside, you might want to also consider making your filters Predicate<Person> instead of Consumer<Person> and moving the results handling elsewhere. This will give you more flexibility as things get more complicated.
Got into a very basic issue. I have to convert json string to objects. I have a custom method as below which is expected to convert into corresponding class and throw an exception if it is not able to get the object out of it.
protected <T> T getObjectFromJson(Class<T> c, String json){
try{
Gson gson = new Gson();
T object = gson.fromJson(json, c);
return object;
} catch (Exception e){
throw new TMMIDClassConversionException(e.getCause(), e.getMessage());
}
}
The issue is this method is not throwing exception if I am trying to convert json of a different class.
My class
public class CompanyCategoryMap {
private Integer id;
private int mid;
private String catKey;
private String catValue;
private int priority;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public int getMid() {
return mid;
}
public void setMid(int mid) {
this.mid = mid;
}
public String getCatKey() {
return catKey;
}
public void setCatKey(String catKey) {
this.catKey = catKey;
}
public String getCatValue() {
return catValue;
}
public void setCatValue(String catValue) {
this.catValue = catValue;
}
public int getPriority() {
return priority;
}
public void setPriority(int priority) {
this.priority = priority;
}
}
When I pass json string of Company rather than String of above class, it does not throw exception.
The string:
"{\"id\":6,\"name\":\"abc\",\"usersCount\":10,\"mid\":3,\"createdAt\":\"Sep 15, 2014 7:02:19 PM\",\"updatedAt\":\"Sep 15, 2014 7:02:19 PM\",\"active\":true,\"currency\":\"abc\",\"source\":\"unknown\",\"user_id\":1,\"tierId\":1}"
I think I am doing this conversion in a wrong way. What is the suggested way of doing it?
Take for example:
class Foo {
private String value;
}
class Bar {
private String value;
}
and
String json = "{\"value\" : \"whatever\"}";
new Gson().fromJson(json, Foo.class);
new Gson().fromJson(json, Bar.class);
Why should Gson reject any of these?
Gson is setup to perform a best effort to deserialize the given JSON into an instance of the given Class. It will map as many fields as it finds. If none are found, that's too bad.
Other libraries like Jackson do the opposite. By default, Jackson rejects any JSON which doesn't contain a mapping for every given class property. You can also configure it to ignore some properties.
Keep doing what you are doing. As the application writer, you should know when to use a Class instance with the appropriate JSON source.
I'm trying to figure out how to dynamically call a method. I have a string that describes the method name, but I'm not sure how to do it. I thought this could be done with reflection, but haven't had any success. Example
set.add(vehicleConfiguration.getVehicleYear.getName());
set.add(vehicleConfiguration.getVehicleMake().getName());
set.add(vehicleConfiguration.getVehicleModel().getName());
You'll notice all the method calls are the same with the exception of the getVehicleYear, etc
I have a string that describes the method names, just not sure how to use it.
I got as far as this with reflection, but failed.
set.add(Class.forName("VehicleConfiguration").getMethod("vehicleMake", null).getName());
Thanks in advance.
The class you are looking for is Method. Please read the appropriate javadoc carefully.
You can get a method with, for example
// assumign `getVehicleMake` is the name of the method and it accepts no parameters
Method method = VehicleConfiguration.class.getMethod("getVehicleMake");
// VehicleConfiguration.class can be replaced by
// Class.forName("VehicleConfiguration")
// if VehicleConfiguration is the fully qualified, ie. with packages, name of the class
// other you need Class.forName("com.yourpackage.VehicleConfiguration")
You then need to invoke() this method on an instance of your class.
VehicleConfiguration instance = new VehicleConfiguration();
Object returnObject = method.invoke(instance); // assuming no parameters
To then call getName(), you need to cast the returned object to the type that has the method. Assuming getMake() is a method of the type VehicleMake, call it like this
((VehicleMake)returnObject).getMake();
You have to use actual method name: getVehicleMake, not vehicleMake.
Additionally, if you're using this as anything other than an exercise, don't roll your own. Use Commons BeanUtils or Spring's BeanWrapper.
Expanding on my comment, As all the methods you showed have a getName() method, let's create a simple class which defines this:
class Nameable
{
private String name;
public Nameable(final String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
}
Now when you create the object for Make, Model and Year, they can all use this class so they can be used interchangeably, and can then be combined into a Car:
class Car
{
public final Nameable make;
public final Nameable model;
public final Nameable year;
public Car(Nameable make, Nameable model, Nameable year)
{
this.make = make;
this.model = model;
this.year = year;
}
public Nameable getInfo(final String info)
{
switch(info)
{
case "make": return this.make;
case "model": return this.model;
case "year": return this.year;
}
return null;
}
}
Then a simple implementation would be:
class PaganiZonda2006 extends Car
{
public PaganiZonda2006()
{
super(new Nameable("Pagani"), new Nameable("Zonda"), new Nameable("2006"));
}
}
And finally, when you want to get the information out, you can read it like so:
public static void main(String[] args)
{
Car car = new PaganiZonda2006();
System.out.println(car.getInfo("make").getName()); //Pagani
System.out.println(car.getInfo("model").getName()); //Zonda
System.out.println(car.getInfo("year").getName()); //2006
}
This ended up being my final solution which is a combination of MrLore and Sotirios Delimanolis solutions. This solution is completely dynamic without the use of any conditions.
This class performs the search for the name by passing in the property name;
String propertyName = "vehicleYear";
vehicleConfiguration.getInfo(propertyName).getName()
propertyName = "vehicleMake";
vehicleConfiguration.getInfo(propertyName).getName()
This class represents the VehicleConfiguration
#Entity
public class VehicleConfiguration extends StatefulEntity {
#ManyToOne
#JoinColumn(name = "year_id")
private VehicleYear vehicleYear;
#ManyToOne
#JoinColumn(name = "make_id")
private VehicleMake vehicleMake;
public LookupBaseEntity getInfo(final String fieldName) {
try {
String methodName = WordUtils.capitalize(fieldName);
Method method = VehicleConfiguration.class.getMethod("get" + methodName);
return (LookupBaseEntity) method.invoke(this);
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
Logger.getLogger(VehicleConfiguration.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
This class represents the VehicleYear
#Entity
public class VehicleYear extends LookupBaseEntity {
}
This class represents the VehicleMake
#Entity
public class VehicleMake extends LookupBaseEntity {
}
Which both extend LookupBaseEntity
public class LookupBaseEntity extends StatefulEntity {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
springframework.beans.BeanUtils is very useful to copy objects, and I use the "ignoreProperties" option frequently. However, sometimes I want to copy only specific objects (basically, the opposite of "ignore Properties"). Does anyone know how can I do that? Any help will be appreciated.
import org.springframework.beans.BeanUtils;
public class Sample {
public static void main(String[] args) {
DemoADto demoADto = new DemoADto();
demoADto.setName("Name of Demo A");
demoADto.setAddress("Address of Demo A");
DemoBDto demoBDto = new DemoBDto();
// This is "ignoreProperties" option
// But I want to know how I can copy only name field by using BeanUtils or something.
BeanUtils.copyProperties(demoADto, demoBDto, new String[] {"address"});
System.out.println(demoBDto.getName());
System.out.println(demoBDto.getAddress());
}
}
public class DemoADto {
private String name;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
public class DemoBDto {
private String name;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
You can use the BeanWrapper technology. Here's a sample implementation:
public static void copyProperties(Object src, Object trg, Iterable<String> props) {
BeanWrapper srcWrap = PropertyAccessorFactory.forBeanPropertyAccess(src);
BeanWrapper trgWrap = PropertyAccessorFactory.forBeanPropertyAccess(trg);
props.forEach(p -> trgWrap.setPropertyValue(p, srcWrap.getPropertyValue(p)));
}
Or, if you really, really want to use BeanUtils, here's a solution. Invert the logic, gather excludes by comparing the full property list with the includes:
public static void copyProperties2(Object src, Object trg, Set<String> props) {
String[] excludedProperties =
Arrays.stream(BeanUtils.getPropertyDescriptors(src.getClass()))
.map(PropertyDescriptor::getName)
.filter(name -> !props.contains(name))
.toArray(String[]::new);
BeanUtils.copyProperties(src, trg, excludedProperties);
}
If you don't want to use Commons BeanUtils you can still use Spring by using the BeanWrapper.
You will have to manually loop through all the properties so you will want to make a static helper method.
You can always just copy what copyProperties is doing and adjust to your liking:
http://tinyurl.com/BeanUtils-copyProperties
Check this out: BeanPropertyCopyUtil.
Example:
copyProperties(user, systemUser, "first firstName", "last lastName");
You'll also need Apache Commons BeanUtils.
beanUtils has an overloaded method wherein we can pass an array of fields that we want to ignore.
Eg.
String[] ignoreProperties= new String[]{"field1","field2"};
BeanUtils.copyProperties(a, b,ignoreProperties);
You may use
org.springframework.beans.BeanUtils.copyProperties(Object source, Object target, Class editable) throws BeansException
Ensure the target implements the interface editable which defines the properties which would be copied.
Here is an Example with Spring BeanUtils class:
public static void copyList(List sourceList,
List targetList, Class targetType) {
try {
for (Object source : sourceList) {
Object target = null;
target = targetType.newInstance();
BeanUtils.copyProperties(source, target);
targetList.add(target);
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}