I am trying to write my own bean utils converter so that I can export my object to a plain text file
I have the main class
public class BeanUtilsTest {
public static void main(String[] args) {
try{
MyObject myObject = new MyObject();
myObject.setId(3l);
myObject.setName("My Name");
ConvertUtilsBean cub = new ConvertUtilsBean();
cub.deregister(String.class);
cub.register(new MyStringConverter(), String.class);
cub.deregister(Long.class);
cub.register(new MyLongConverter(), Long.class);
System.out.println(cub.lookup(String.class));
System.out.println(cub.lookup(Long.class));
BeanUtilsBean bub = new BeanUtilsBean(cub, new PropertyUtilsBean());
String name = bub.getProperty(myObject, "name");
System.out.println(name);
String id = bub.getProperty(myObject, "id");
System.out.println(id);
}catch(Exception ex){
ex.printStackTrace();
}
}
}
The Long Converter
public class MyLongConverter implements Converter{
#Override
public Object convert(Class clazz, Object value) {
System.out.println("Long convert");
return value.toString()+"l";
}
}
The String Converter
public class MyStringConverter implements Converter{
#Override
public Object convert(Class clazz, Object value) {
System.out.println("String convert");
return value.toString()+":";
}
}
Finally my object
public class MyObject {
Long id;
String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The Output
String convert
My Name:
String convert
3:
I was expecting the id will go through MyLongConverter, but it seems it is still going thru the String one. Why and how can I fix this?
Please advise
Thanks
String id = bub.getProperty(myObject, "id");
Above getProperty function in BeanUtilBean class has to return String representation of the property you requested, regardless of what format the property is defined. So, it will always use String converter (MyStringConverter).
Since the destination type here is always String, MyLongConverter will never be used.
Instead, MyStringConverter should inspect the type of the value parameter and accordingly convert it to String.
Related
I have a Gender enum (which I can't edit) which is used in a Java class. Now, when I need to Serialise the Java class to JSON, I want the value of the Enum as part of the JSON and not the Enum name. For example below in my enum, and when this enum is serialized, I want the value as {"gender":"Male"} I am using:
String underWritingJSONString = objectMapper.writeValueAsString(myObject);
public enum Gender {
MALE("Male"),
FEMALE("Female");
Gender(String gender) {
this.name = gender;
}
private String name;
String toValue() {
return name;
}
}
expected result = {"gender":"Male"}
current result = {"gender":"MALE"}
Following is the sample class
public class MyObject {
#JSONField
public Gender gender;
public MyObject() {
}
public Gender getGender() {
return this.gender;
}
}
If enum has toString() method which returns the value then
mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
Is enough. But your enum don't have that as #Dinesh Kondapaneni mentioned you shold write a custom serializer like
class GenderSerializer extends JsonSerializer<Gender> {
#Override
public void serialize(Gender value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (null == value) {
} else {
try {
gen.writeString(value.toValue());
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
}
}
And use it in your MyObject as
#JsonSerialize(using = GenderSerializer.class)
public Gender gender;
FYI: Am using jackson here
You need to add a method with annotation #JsonValue:
enum Gender {
MALE("Male"),
FEMALE("Female");
Gender(String gender) { // constructor
this.name = gender;
}
private String name; // variable
#JsonValue
String toValue() {
return name;
}
}
This method will be called when you are serializing your object to JSON.
(Jackson 2.6.2 and above) you can now simply write:
public enum Gender {
#JsonProperty("Male")
MALE,
#JsonProperty("Female")
FEMALE
}
Using Retrofit here to consume Google Civic API.
The library requires you to create a model of what the API will return as I have done already with Election. Which is basically a copy of the google documentation.
(Retrofit binds the response properties to properties with the same name)
Election.Java :
public class Election {
private long id;
private String name;
private String electionDay;
private String ocdDivisionId;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getElectionDay() {
return electionDay;
}
public void setElectionDay(String electionDay) {
this.electionDay = electionDay;
}
public String getOcdDivisionId() {
return ocdDivisionId;
}
public void setOcdDivisionId(String ocdDivisionId) {
this.ocdDivisionId = ocdDivisionId;
}
}
But Representatives have an inconsistent property name, thus I don't see a way to model this in a way Retrofit will know how to deserialize the API's response.
Representatives object (JSON) :
property name is called (key)
How do I let Retrofit deserialize a model that captures the property named variable after a key of the division?
Assuming you're using a Gson converter, I personally would use a map. I guess the same can be achieved with other converters, but I never used them. Say you have the following object:
public class Division {
#SerializedName("name")
#Expose
private String name;
#SerializedName("alsoKnownAs")
#Expose
private List<String> alsoKnownAs = new ArrayList<>();
#SerializedName("officeIndices")
#Expose
private List<Integer> officeIndices = new ArrayList<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getAlsoKnownAs() {
return alsoKnownAs;
}
public void setAlsoKnownAs(List<String> alsoKnownAs) {
this.alsoKnownAs = alsoKnownAs;
}
public List<Integer> getOfficeIndices() {
return officeIndices;
}
public void setOfficeIndices(List<Integer> officeIndices) {
this.officeIndices = officeIndices;
}
}
Which represents the object inside the divisions array. You can then have the class:
private class Divisions {
#SerializedName("divisions")
#Expose
private Map<String, Division> divisions = new HashMap<>();
// ...
}
Notice the usage of a map here? Behind the scenes Gson will be able to serialise and deserialise your objects. The class Divisions is the root of the json you gave us in the question.
Hope this helps
This is a follow up question to this question:
Passing custom type query parameter
I got a class which includes this method:
public static MyClass fromString(String json)
{
Gson gson = new Gson();
MyClass user = gson.fromJson(json, MyClass.class);
return user;
}
The full class:
public class MyClass
{
public String name;
public PortalNameEnum portalName;
public PortalUserTypeEnum portalUserType;
public String notes;
public MyClass(String name, PortalNameEnum portalName,
PortalUserTypeEnum portalUserType, String notes)
{
super();
this.portalName = portalName;
this.portalUserType = portalUserType;
this.name = name;
this.notes = notes;
}
public static MyClass fromString(String json)
{
Gson gson = new Gson();
PortalUserInfo user = gson.fromJson(json, PortalUserInfo.class);
return user;
}
public PortalNameEnum getPortalName()
{
return portalName;
}
public void setPortalName(PortalNameEnum portalName)
{
this.portalName = portalName;
}
public PortalUserTypeEnum getPortalUserType()
{
return portalUserType;
}
public void setPortalUserType(PortalUserTypeEnum portalUserType)
{
this.portalUserType = portalUserType;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getNotes()
{
return notes;
}
public void setNotes(String notes)
{
this.notes = notes;
}
}
I got a resource which got a method:
#Path("/myclasscall")
#GET
#UnitOfWork
public String registerPortalUser(#Context HttpServletRequest req, #QueryParam("callback") String callback, #QueryParam("myclass") MyClass recordData) throws Throwable
{ .. }
It seems like the fromString method is not called and the resource method is always null, even though I see in the console the request itself and I do see a string that has been passed. Why is that?
The problem was with the client.
Instead of passing a single parameter called "myclass", he passed all the fields separately. After merging them together into a single Json instance, it was fixed.
I'm being given a Json file with the form:
{
"descriptions": {
"desc1": "someString",
"desc2": {"name":"someName", "val": 7.0}
}
}
I have the POJO:
public class CustomClass {
Map<String, Object> descriptions;
public static class NameVal{
String name;
double val;
public NameVal(String name, double val){...}
}
}
I can recreate the json file with the code:
CustomClass a = new CustomClass();
a.descriptions = new HashMap<String, Object>();
a.descriptions.put("desc1", "someString");
a.descriptions.put("desc2", new CustomClass.NameVal("someName", 7.0));
new ObjectMapper().writeValue(new File("testfile"), a);
But, when I read the object back in using:
CustomClass fromFile = new ObjectMapper().readValue(new File("testfile"), CustomClass.class);
then fromFile.descriptions.get("desc2") is of type LinkedHashMap instead of type CustomClass.NameVal.
How can I get Jackson to properly parse the type of the CustomClass.NameVal descriptors (other than making some class that wraps the parsing and explicitly converts the LinkedHashMap after Jackson reads the file)?
Try this. Create a class Description with name and value attributes:
public class Description {
private String name;
private double val;
}
Now in your CustomClass do this:
public class CustomClass {
List<Description> descriptions;
}
And that's it. Remember to create getters and setters because Jackson needs it.
You could try something like this:
public class DescriptionWrapper {
private Description descriptions;
public Description getDescriptions() {
return descriptions;
}
public void setDescriptions(Description descriptions) {
this.descriptions = descriptions;
}
}
public class Description {
private String desc1;
private NameValue desc2;
public String getDesc1() {
return desc1;
}
public void setDesc1(String desc1) {
this.desc1 = desc1;
}
public NameValue getDesc2() {
return desc2;
}
public void setDesc2(NameValue desc2) {
this.desc2 = desc2;
}
}
public class NameValue {
private String name;
private double val;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getVal() {
return val;
}
public void setVal(double val) {
this.val = val;
}
}
I have a very simple problem-
I have a class named DEClient whose constructor is like this-
public DEClient(List<DEKey> keys) {
process(keys);
}
And DEKey class is like this-
public class DEKey {
private String name;
private String value;
public DEKey(){
name = null;
value = null;
}
public DEKey(String name, String value){
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
Now I am trying to instantiate DEClient constructor. So for that I need to have List<DEKey>.
So what I did is I instantiated DEKey class like below using service.getKeys() (which will return String) and id as the value.
DEKey dk = new DEKey(service.getKeys(), id);
//The below line throws exception whenever I am running.
DEClient deClient = new DEClient((List<DEKey>) dk);
What wrong I am doing here?
You need to first make a List, then add your key to that List. Casting like you have done is not the way to do it, since DEKey is not a List and casting into it will throw a ClassCastException.
DEKey dk = new DEKey(service.getKeys(), id);
List<DEKey> list = new ArrayList<DEKey>();
list.add (dk);
DEClient deClient = new DEClient(list);