example:
public class User{
private String name;
private String age;
private String address;
.... set and get methods....
}
User user = new User();
user.setName("albert");
user.setAge("22");
i want convert user object to this string
name=albert&address=&age=22
Because i use new URL() to post this string.
I have much other object to convert to like this string,so i want a tool method
Is any good way to do?
Thanks in advance
Using reflection for any Object type:
import java.lang.reflect.Field;
public class ObjectToPostStringbyReflection {
public String getPostParamString(Object o) throws IllegalArgumentException, IllegalAccessException{
Class<? extends Object> clazz = o.getClass();
StringBuilder sb = new StringBuilder();
for(Field f : clazz.getDeclaredFields()){
f.setAccessible(true);
if(!sb.toString().isEmpty()){
sb.append("&");
}
sb.append(f.getName())
.append("=")
.append(f.get(o));
}
return sb.toString();
}
/**
* #param args
* #throws IllegalAccessException
* #throws IllegalArgumentException
*/
public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
// TODO Auto-generated method stub
User user = new User();
user.setName("John");
user.setAddress("address");
user.setAge("18");
ObjectToPostStringbyReflection reflector = new ObjectToPostStringbyReflection();
System.out.println(reflector.getPostParamString(user));
}
}
Result: name=John&age=18&address=address
You could try to find a framework which will do this for you (e.g. Spring), or you could just bite the bullet and write your own method:
public String toPost() {
StringBuilder sb = new StringBuilder();
sb.append("name=")
.append(this.name)
.append("&address=")
.append(this.address)
.append("&age=")
.append(this.age);
return sb.toString();
}
A simple method would be this:
public class user{
private String name;
private String age;
private String address;
.... set and get methods....
public String toText() {
return "name="+this.name+"&adress="+this.adress+"&age="+this.age;
}
}
If performance is important you can use StringBuilder
String postString = "name="+user.name+"&address="+user.address+"&age="+user.age;
There are no. of ways of doing this. it could be best for you to choose!
If you want to convert your object to pass by query String by Rest. It'll be impossible but if you want only a return that string when you use the toString() method. you need to overwriting the toString method in your class.
Like that:
#Override
public String toString() {
return "name="+ name + "&address=" +adress+ "&age=" +age+
}
Related
I have a Strig variable.
String name = "xyz";
I want to convert it to json object
{"name" : "xyz"}
I am currently doing it by putting the name in the map and converting map to json.
Map<String, String > map = new HashMap<>();
map.put("name", name);
new Gson().toJson(map);;
Is there better way to do it?
You can use ObjectMapper from com.fasterxml.jackson as
public static String getString(Object object) {
try {
//Object to JSON in String
return objectMapper.writeValueAsString(object);
} catch (IOException e) {
log.error("Exception ", e);
return "";
}
}
You should create an Class Name. So it will automatically create mapping for you:
Sample Code:
public static void main(String[] args) {
Name name = new Name();
name.setName("xyz");
System.out.println(new Gson().toJson(name));
}
Output: {"name":"xyz"}
Name Class:
public class Name {
private String name;
/**
* #return the name
*/
public String getName() {
return name;
}
/**
* #param name
* the name to set
*/
public void setName(String name) {
this.name = name;
}
}
If you have a JSON object of any complexity, you should create a POJO to hold it for you as suggested by #notescrew. If this is a one-off (for example, in some Web controllers that return basic status information) and you are on at least Java 9, you can simplify it by using an inline map:
gson.toJson(Map.of("name", name));
Note that most frameworks (client and server) will take care of serializing to JSON for you, so in most cases you can simply pass or return the Map directly.
I have a fairly basic Java class with some class variables. I have overwridden toString() to provide me with a string output (which will eventually be output to a text file).
I am trying to elegantly create a way for me to use this string output to recreate the object with all of the variables set as before. The class looks something like this:
public class Report {
private String itemA;
private String itemB;
private String itemC;
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Items are::");
sb.append("\nItem A is: ").append(itemA);
sb.append("\nItem B is: ").append(itemB);
sb.append("\nItem C is: ").append(itemC);
return sb.toString();
}
}
this is how I can potentially tackle it using reflection:
public class Report {
private String itemA;
private String itemB;
private String itemC;
private final Map<String, String> MAPPING = new HashMap<>();
public Report(String itemA, String itemB, String itemC) {
this.itemA = itemA;
this.itemB = itemB;
this.itemC = itemC;
MAPPING.put("Item A is: ", "itemA");
MAPPING.put("Item B is: ", "itemB");
MAPPING.put("Item C is: ", "itemC");
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Items are::");
MAPPING.entrySet().forEach(entry -> {
sb.append("\n").append(entry.getKey()).append(BeanUtils.getProperty(this, entry.getValue()));
});
return sb.toString();
}
public Report createReportFromString(String reportString) {
List<String> reportLines = Arrays.asList(reportString.split("\n"));
HashMap<String, String> stringObjectRelationship = new HashMap<>();
reportLines.forEach(reportLine -> {
Optional<String> matchingKey = MAPPING.keySet().stream().filter(reportLine::contains).findFirst();
matchingKey.ifPresent(key -> {stringObjectRelationship.put(MAPPING.get(key), reportLine.split(key)[1]);});
});
stringObjectRelationship.forEach((variableName, variableValue) -> BeanUtils.setProperty(this, variableName, variableValue));
return this;
}
}
I basically want to relate the key in the report ("Item A is: ") to the name of the corresponding variable ("itemA") and use this relationship in both the toString() method and the createReportFromString(String string) method. Now when doing this there are a lot of possible exceptions that can be thrown and need to either be handled or thrown - and it then looks a lot less elegant than I would like.
I don't know if this is possible to do without reflection - or perhaps I could rearrange this class to make this possible?
What I can`t change is the structure of the string output in the toString().
Reflection bears multiple features:
Automatic discovery of features of a program at runtime
Support for dealing with features unknown at compile-time
Provide an abstraction of program features (e.g. methods or fields)
Your approach suggests that you don’t want an automatic discovery, as you are specifying the three elements explicitly. This is a good thing, as it makes your program more robust regarding future changes, as dealing with automatically discovered, potentially unknown program elements will destroy any help from the compiler, as it can’t tell you when there are mismatches.
You only want the third point, an abstraction over the elements of your report. You can create such an abstraction yourself, tailored to your use case, without Reflection, which will be more robust and even more efficient:
public class Report {
static final class Element {
final String header;
final Function<Report,String> getter;
final BiConsumer<Report,String> setter;
final Pattern pattern;
Element(String header,
Function<Report, String> getter, BiConsumer<Report, String> setter) {
this.header = header;
this.getter = getter;
this.setter = setter;
pattern = Pattern.compile("^\\Q"+header+"\\E(.*?)$", Pattern.MULTILINE);
}
}
static final List<Element> ELEMENTS = List.of(
new Element("Item A is: ", Report::getItemA, Report::setItemA),
new Element("Item B is: ", Report::getItemB, Report::setItemB),
new Element("Item C is: ", Report::getItemC, Report::setItemC));
private String itemA, itemB, itemC;
public Report(String itemA, String itemB, String itemC) {
this.itemA = itemA;
this.itemB = itemB;
this.itemC = itemC;
}
#Override public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Items are:");
ELEMENTS.forEach(e ->
sb.append('\n').append(e.header).append(e.getter.apply(this)));
return sb.toString();
}
public static Report createReportFromString(String reportString) {
return new Report("", "", "").setValuesFromString(reportString);
}
public Report setValuesFromString(String reportString) {
Matcher m = null;
for(Element e: ELEMENTS) {
if(m == null) m = e.pattern.matcher(reportString);
else m.usePattern(e.pattern).reset();
if(!m.find())
throw new IllegalArgumentException("missing \""+e.header+'"');
e.setter.accept(this, m.group(1));
}
return this;
}
public String getItemA() {
return itemA;
}
public void setItemA(String itemA) {
this.itemA = itemA;
}
public String getItemB() {
return itemB;
}
public void setItemB(String itemB) {
this.itemB = itemB;
}
public String getItemC() {
return itemC;
}
public void setItemC(String itemC) {
this.itemC = itemC;
}
}
This works with Java’s out-of-the-box features, not requiring another library to simplify the operation.
Note that I changed the code pattern, as createReportFromString is a misleading name for a method modifying an already existing object. I used the name for a factory method truly creating a new object and added a another method for setting the values of the object (as a direct counter-part to toString).
If you are still using Java 8, you can replace List.of(…) with Arrays.asList(…) or better Collections.unmodifiableList(Arrays.asList(…)).
You can also remove the .reset() call in the setValuesFromString method. When you remove it, the elements in the input string are required to be in the same order as the toString() method produces. This makes it a bit less flexible, but also more efficient if you expand the code to have a lot more elements.
#JimboMcHiggins assuming I can change the toString output how exactly would you tie together serialization and deserialization with some common mapping?
I would leave the toString unchanged and move the responsibility of serialization to java.io.Serializable. Correct me if this is not an acceptable approach. The mapping would be defined by the class fields of your Report pojo. This would also allow you to change your toString without breaking deserialization of existing objects.
import java.io.Serializable;
public class Report implements Serializable {
private static final long serialVersionUID = 1L;
private String itemA;
private String itemB;
private String itemC;
public Report(String itemA, String itemB, String itemC) {
this.itemA = itemA;
this.itemB = itemB;
this.itemC = itemC;
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Items are::");
sb.append("\nItem A is: ").append(itemA);
sb.append("\nItem B is: ").append(itemB);
sb.append("\nItem C is: ").append(itemC);
return sb.toString();
}
}
Example Usage
public class Test1 {
public static void main(String[] args) {
Report report = new Report("W", "O", "W");
System.out.println(report);
String filename = "file.ser";
// Serialization
try
{
//Saving of report in a file
FileOutputStream file = new FileOutputStream(filename);
ObjectOutputStream out = new ObjectOutputStream(file);
// Method for serialization of report
out.writeObject(report);
out.close();
file.close();
System.out.println("Report has been serialized");
}
catch(IOException ex)
{
System.out.println("IOException is caught");
}
Report report1 = null;
// Deserialization
try
{
// Reading the report from a file
FileInputStream file = new FileInputStream(filename);
ObjectInputStream in = new ObjectInputStream(file);
// Method for deserialization of report
report1 = (Report)in.readObject();
in.close();
file.close();
System.out.println("Report has been deserialized ");
System.out.println(report1);
}
catch(IOException ex)
{
System.out.println("IOException is caught");
}
catch(ClassNotFoundException ex)
{
System.out.println("ClassNotFoundException is caught");
}
}
}
Output
Items are::
Item A is: W
Item B is: O
Item C is: W
Report has been serialized
Report has been deserialized
Items are::
Item A is: W
Item B is: O
Item C is: W
Eclipse gave option to auto generate the toString method for every class.
Further leverage this facility, I am creating String Format Template to give as Json format when eclipse generate toString Method.
I used following String Format Template:
{ ${member.name()}:"${member.value}", ${otherMembers}}
now i generated toString method as following POJO but When i run this program i got result as and not a VALID JSON.
{ name:"null", reportees:"[1, 2, 3]", department:"[retail, banking, finance]", owns:"null", supplimentary:"null}
Code
public class TestPojo {
private String name;
private List<String> reportees;
private String[] department;
private Machine owns;
private List<Machine> supplimentary;
public static void main(String arg[]) {
TestPojo aTestPojo = new TestPojo();
aTestPojo.department = new String[] { "retail", "banking", "finance" };
aTestPojo.reportees = new ArrayList<String>() {
{
add("1");
add("2");
add("3");
}
};
System.out.print(aTestPojo);
}
public static class Machine {
private String machineName;
private String duties;
public String getMachineName() {
return machineName;
}
public void setMachineName(String machineName) {
this.machineName = machineName;
}
public String getDuties() {
return duties;
}
public void setDuties(String duties) {
this.duties = duties;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("{ machineName:\"").append(machineName).append("\", duties:\"").append(duties).append("}");
return builder.toString();
}
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("{ name:\"").append(name).append("\", reportees:\"").append(reportees).append("\", department:\"").append(Arrays.toString(department)).append("\", owns:\"").append(owns).append("\", supplimentary:\"").append(supplimentary).append("}");
return builder.toString();
}
}
With the help of #dvlcube idea. I built a "Eclipse Custom toString() builder" to generate toString method code to return a JSON formatted string of current object.
Follow the github for this solution Click [Eclipse toString_Builder for JSON](https://github.com/djaganathan/EclipseToStringBuilderForJson/blob/master/src/main/java/com/github/djaganathan/opensource/eclipse/util/JsonToStringBuilder.java,"Custom Eclipse toString() Builder")
Sample Testing Code
import com.google.common.collect.Maps;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.time.StopWatch;
import org.elasticsearch.common.collect.Lists;
public class TestPojo {
private String name;
private List<String> reportees;
private String[] department;
private Machine owns;
private List<Machine> supplimentary;
private int i = 10;
private Map<String, Machine> machineList = Maps.newConcurrentMap();
public static void main(String arg[]) {
TestPojo aTestPojo = new TestPojo();
aTestPojo.department = new String[] { "retail", "banking", "finance"};
aTestPojo.reportees = new ArrayList<String>() {
{
add("1");
add("2");
add("3");
}
};
Machine aMachine = new Machine("Train", "travel");
Machine aMachine1 = new Machine("Lorry", "Carrier");
aTestPojo.supplimentary = Lists.newArrayList(aMachine, aMachine1);
aTestPojo.machineList.put("Train", aMachine);
aTestPojo.machineList.put("Lorry", aMachine1);
System.out.print(aTestPojo);
}
public static class Machine {
private String machineName;
private String duties;
public Machine(String machineName, String duties) {
super();
this.machineName = machineName;
this.duties = duties;
}
public String getMachineName() {
return machineName;
}
public void setMachineName(String machineName) {
this.machineName = machineName;
}
public String getDuties() {
return duties;
}
public void setDuties(String duties) {
this.duties = duties;
}
#Override
public String toString() {
JsonToStringBuilder builder = new JsonToStringBuilder(this);
builder.append("machineName", machineName);
builder.append("duties", duties);
return builder.build();
}
}
#Override
public String toString() {
JsonToStringBuilder builder = new JsonToStringBuilder(this);
builder.append("name", name);
builder.append("reportees", reportees);
builder.append("department", department);
builder.append("owns", owns);
builder.append("supplimentary", supplimentary);
builder.append("i", i);
builder.append("machineList", machineList);
String value = builder.build();
return value;
}
}
While running this program i got the following JSON output
{"name": null,"reportees": ["1","2","3"],"department": ["retail","banking","finance"],"owns": null,"supplimentary": [{"machineName": "Train","duties": "travel"},{"machineName": "Lorry","duties": "Carrier"}],"i": 10,"machineList": {"Lorry": {"machineName": "Lorry","duties": "Carrier"},"Train": {"machineName": "Train","duties": "travel"}}}
It's not valid JSON because of the way arrays and collections are being printed ("[1,2,3]" instead of ["1","2","3"]).
Also, it wouldn't pass strict JSON validation because the field names should be quoted as well.
Eclipse's String Format Template can be very useful, but for full control it's better to create a builder class.
Here's a gist for doing just that. You can expand on it, and it works for your example class out of the box.
You can use this class to generate the toString() methods in Eclipse.
Recommended only for String values. Below expression gives you as a JSON toString generation.
{"${member.name()}":"${member.value}", "${otherMembers}"}
I want to use the method execute() of the following class:
public class Parser {
#Header("header1")
private String attribute1;
#Header("header2")
private String attribute2;
#Header("header3")
private String attribute3;
#Header("header4")
private String attribute4;
public String execute(String headerValue) {
//Execute
}
}
What I want this method to achieve is matching the headerValue parameter with one in the list of #Header annotations, and returning the value of the respective attribute. For example, if I call execute("header3"), it should return the value of attribute3
How can I achieve this? Or is it a better way to code this requirement?
Why don't you just use a map for this? You'd need one anyways in order to store the mapping of the annotation parameter value to the field but if you can do this without reflection it should be easier to code and to maintain.
What I mean is:
Map<String, String> attributes; //initialized
attributes.put("header1", value1);
...
In execute() you then just access the map.
You could improve this using an enum, e.g. in order to restrict the number of possible values.
Something like this:
enum HeaderType {
HEADER1,
HEADER2,
...
}
private Map<HeaderType, String> headerAttribs = ...;
void setAttrib( HeaderType type, String value ) {
headerAttribs.put(type, value);
}
String getAttrib( HeaderType type ) {
return headerAttribs.get(type);
}
public String execute(HeaderType type ) {
//Execute
}
If you need to use a string for the header type you could consider employing an additional map string->header type to look up the correct type first.
Alternatively you could use a switch statement which since Java 7 should work with strings as well.
Try this:
public String execute(String headerValue) throws IllegalArgumentException, SecurityException, IllegalAccessException, NoSuchFieldException {
for(Field field:this.getClass().getFields()) {
if (field.isAnnotationPresent(Header.class)) {
Header annotation = field.getAnnotation(Header.class);
String name = annotation.value();
if(name.equals(headerValue)) {
Object val = this.getClass().getField(name).get(this);
return (String) val;
}
}
}
return null;
}
There are a couple of exception to handle in line:
Object val = this.getClass().getField(name).get(this);
You can return null for that exception if you don't want to throw it from this method.
This may help you
Field f[]= Parser.class.getDeclaredFields();
for (int i = 0; i < f.length; i++) {
Annotation annotation[]= f[i].getAnnotations();
for (int j=0;j<annotation.length;j++){
Class<Annotation> type = (Class<Annotation>) annotation[j].annotationType();
for (Method method : type.getDeclaredMethods()) {
if(method.getName() .equals(headerValue))
{
String name=f[i].getName();
return name;
}
}
}
}
Parser.class.getDeclaredFields() will include private fields also.
This question already has answers here:
gson: Treat null as empty String
(6 answers)
Closed 3 years ago.
I'm using gson to serialize some object. I have a requirement that any null field should be treated as an empty string, independent on the variable being a string, double or a pojo.
I tried to create a custom serializer for Object and simply return a new JsonPrimitive("") for the null valued objects, but the problem is how to handle the non-null valued objects without the use of "instanceof" or "getClass" and handling every single type.
Any thoughts on how to do this is appreciated.
This can be done using a custom TypeAdaptor for your model Object.
You can iterate over the object field using reflection and whenever a field is null, set the value in the json representation to an empty string whenever you cross a null field.
This would absolutely be hard to maintain and should be done with some risks as #Sotirios Delimanolis stated, What if the corresponding reference is not a String, how are you going to intending to handle it back and forth?
Here is a bean structure just used to showcase the situation:
public class MyEntity
{
private int id;
private String name;
private Long socialNumber;
private MyInnerEntity innerEntity;
public MyEntity(int id, String name, Long socialNumber, MyInnerEntity innerEntity)
{
this.id = id;
this.name = name;
this.socialNumber = socialNumber;
this.innerEntity = innerEntity;
}
public int getId()
{
return id;
}
public String getName()
{
return name;
}
public Long getSocialNumber()
{
return socialNumber;
}
public MyInnerEntity getInnerEntity()
{
return innerEntity;
}
public static class MyInnerEntity {
#Override
public String toString()
{
return "whateverValue";
}
}
}
Here is the TypeAdapter implementation which set any null value to and empty "" String:
public class GenericAdapter extends TypeAdapter<Object>
{
#Override
public void write(JsonWriter jsonWriter, Object o) throws IOException
{
jsonWriter.beginObject();
for (Field field : o.getClass().getDeclaredFields())
{
Object fieldValue = runGetter(field, o);
jsonWriter.name(field.getName());
if (fieldValue == null)
{
jsonWriter.value("");
}
else {
jsonWriter.value(fieldValue.toString());
}
}
jsonWriter.endObject();
}
#Override
public Object read(JsonReader jsonReader) throws IOException
{
/* Don't forget to add implementation here to have your Object back alive :) */
return null;
}
/**
* A generic field accessor runner.
* Run the right getter on the field to get its value.
* #param field
* #param o {#code Object}
* #return
*/
public static Object runGetter(Field field, Object o)
{
// MZ: Find the correct method
for (Method method : o.getClass().getMethods())
{
if ((method.getName().startsWith("get")) && (method.getName().length() == (field.getName().length() + 3)))
{
if (method.getName().toLowerCase().endsWith(field.getName().toLowerCase()))
{
try
{
return method.invoke(o);
}
catch (IllegalAccessException e)
{ }
catch (InvocationTargetException e)
{ }
}
}
}
return null;
}
}
Now a simple straightforward main method to add the adapter to Gson:
public class Test
{
public static void main(String[] args)
{
Gson gson = new GsonBuilder().registerTypeAdapter(MyEntity.class, new GenericAdapter()).create();
Object entity = new MyEntity(16, "entity", (long)123, new MyEntity.MyInnerEntity());
String json = gson.toJson(entity);
System.out.println(json);
Object entity2 = new MyEntity(0, null, null, null);
json = gson.toJson(entity2);
System.out.println(json);
}
}
This would result in below output:
{"id":"16","name":"entity","socialNumber":"123","innerEntity":"whateverValue"}
{"id":"0","name":"","socialNumber":"","innerEntity":""}
Note that whatever the object is of type, its value is set to "" in the marshalled json string.
See gson: Treat null as empty String