I have this class:
public class Campaign
{
String Campaign_ID;
String Campaign_Description;
String startQuestion;
ArrayList Question_Array;
ArrayList workflow;
public Campaign(String ID, String description, String start, ArrayList quesArray, ArrayList workflow)
{
this.Campaign_ID = ID;
this.Campaign_Description = description;
this.startQuestion = start;
this.Question_Array = quesArray;
this.workflow = workflow;
}
public String getID() {return this.Campaign_ID;}
public String getDescription() {return this.Campaign_Description;}
public String getStartQuestion() {return this.startQuestion;}
public ArrayList getQuestionArray() {return this.Question_Array;}
public ArrayList getWorkflow() {return this.workflow;}
public Base_Question getQuestionByID(String ID){
Base_Question result = null;
for (int i=0;i<Question_Array.size();i++)
{
if ( ((Base_Question) Question_Array.get(i)).getQuestionID().equals(ID) )
result = (Base_Question) Question_Array.get(i);
}
return result;
}
}
The ArrayList Question_Array contains a list of instances from other classes.
Then I initialize an instance of Campaign:
Campaign campaign = new Campaign("Cam999","description blabla","Q001",list, workflow);
Then I serialize this instance into text:
Gson gson = new Gson();
String text = gson.toJson(campaign);
To get back the instance of Campaign class, I use:
Campaign campaign2 = gson.fromJson(text, Campaign.class);
I can print things from campaign2 correctly, like campaign2.getID() returns Cam999. But the problem is the ArrayList inside campaign2.
campaign2.getQuestionArray().get(index).getClass() prints com.google.gson.internal.LinkedTreeMap, not my custom class.
My whole source code is here https://github.com/khoiboo/Class_Hierarchy
How to solve this problem. I appreciate your help so much
Related
I have the following entity class:
public class Conversation {
private String id;
private String ownerId;
private Long creationDate;
public Conversation(String id, String ownerId, Long creationDate){
this.id = id;
this.ownerId = ownerId;
this.creationDate = creationDate;
}
}
On other submodule through an external service, on each insertion, I recive a map of the following entities:
public class AttributeValue {
private Sring s; //string attribute
private String n; //number attribute
public String getS() {
return this.s;
}
public String getN() {
return this.n;
}
public AttributeValue(String s, String n){
this.s = s;
this.n = n;
}
}
//Example if I insert this conversation: new Conversation("1", "2", 1623221757971)
// I recive this map:
Map<String, AttributeValue> insertStream = Map.ofEntries(
entry("id", new AttributeValue("1", null)),
entry("ownerId", new AttributeValue("2", null)),
entry("creationDate", new AttributeValue(null, "1623221757971"))
);
To read the ownerId field from the map, I have to do this:
String ownerId = insertStream.get("ownerId").getS();
My question is, instead of have to write: insertStream.get("ownerId"), exists any way through Reflection to read the name of the field from the entity (Conversation.ownerId)?
This is because we want to mantain the submodule and If we make a change on the entitity, for example change ownerId for ownerIdentifier, the submodule shows a compilation error or is changed automatically.
Is this what you want? Field#getName()
Example code:
Field[] conversationFields = Conversation.class.getDeclaredFields();
String field0Name = conversationFields[0].getName();
Depending on the JVM used, field0Name can be "id". You can also use Class#getFields(), this method includes all Fields that are accessible in this class (super class's fields).
Another option (not using reflection) would be to refactor your code.
import java.util.Map;
import java.util.HashMap;
public class Conversation {
public static String[] names = {
"id", "ownerId", "creationDate"
};
private Map<String, Object> data = new HashMap<String,Object>();
public Conversation(Object... data) {
if(data.length!=names.length)
throw new IllegalArgumentException("You need to pass "+names.length+" arguments!");
for(int i=0; i<names.length; i++)
data.put(names[i],data[i]);
}
public Map<String,Object> getData() { return data; }
// You can pass "id"/"ownerId" or names[0]/names[1]
public String getString(String key) {
return (String)data.get(key);
}
// You can pass "creationDate" or names[2]
public long getLong(String key) {
return (long)data.get(key);
}
}
You could then create Conversation Objects like before:
Conversation c = new Conversation("myId","myOwnerId",123456789L);
You could also add public static String fields like ID="id", but changing the value of a field will never change the field's name.
This question already has answers here:
Serialize object using GSON
(2 answers)
Closed 3 years ago.
Code:
public class Crate {
private final MapPosition cratePosition;
private final int tierId;
#Expose(serialize = false, deserialize = false)
private final Inventory inventory;
public Crate(MapPosition cratePosition, int tierId) {
this.cratePosition = cratePosition;
this.tierId = tierId;
this.inventory = Bukkit.createInventory(null, 9*3, "Supply Crate");
}
public void replenishCrates(CrateConfig config) {
List<CrateContent> contents = config.getContentByTier(tierId);
//TODO:
}
public Inventory getInventory() {
return inventory;
}
public Location toLocation(World world) {
return cratePosition.toLocation(world);
}
public MapPosition getCratePosition() {
return cratePosition;
}
public int getTierId() {
return tierId;
}}
The #Expose is being ignored and returning a null pointer exception when trying to deserialize and serialize the class contents. I have made sure to also include the correct GsonBuilder modifications, as stated in Gson's documentation.
The problem you are having is not because #Expose is being ignored but rather because #Expose is missing on the other attributes.
The GsonBuilder's modification you are refering to is the following:
This annotation has no effect unless you build Gson with a GsonBuilder and invoke GsonBuilder.excludeFieldsWithoutExposeAnnotation() method.
But thank God the authors have correctly named the method and it will do just what it is expressing: it will exclude every field that is not marked with the #Expose annotation.
Here is an illustration based on your code (a little bit different because you did not share a completely reproductible sample)
public class Crate {
private final String cratePosition;
private final int tierId;
#Expose(serialize = false, deserialize = false)
private final Inventory inventory;
public Crate(String cratePosition, int tierId) {
this.cratePosition = cratePosition;
this.tierId = tierId;
this.inventory = new Inventory("IV-ID-111000", 10200);
}
public Inventory getInventory() {
return inventory;
}
public int getTierId() {
return tierId;
}
public String getCratePosition() {
return cratePosition;
}
}
And the following test:
public static void main(String[] args) {
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
Crate crate = new Crate("484:125.52", 1250);
String jsonString = gson.toJson(crate);
System.out.println(jsonString);
String json = "{\"cratePosition\":\"4894:125.52\",\"tierId\":2350}";
Crate deserialized = gson.fromJson(json, Crate.class);
System.out.println(deserialized.getCratePosition() + ":" + deserialized.getTierId());
}
In the current case I have no #Expose annotation on cratePosition and tierId fields, so they are excluded from the serialization and deserialization. Therefore my test returns:
{}
null:0
Now let's add the #Expose annotation on the cratePosition and tierId fields in the Crate class:
#Expose()
private final String cratePosition;
#Expose()
private final int tierId;
By default the serialize and deserialize parameters of the #Expose annotation are both set to true. You can play with it and change the values to see the differences it produces.
If I run the test again I have:
{"cratePosition":"484:125.52","tierId":1250}
4894:125.52:2350
What design-pattern, if any, would be most appropriate in this situation.
public class PersonFromDB1 {
private String firstName;
private String lastName;
private String Car;
}
public class PersonFromDB2 {
private String first_name;
private String last_name;
private String boat;
}
Out of these two person types, the only data I would like to work on is fist name and last name regardless of how it field name is name inside the different DBs. firstName and first_name represents the same - name of a person/customer - so does lastName and last-name. The car and boat fields are, in my example, completely irrelevant and should therefore be ignored.
Using, maybe polymorphism or the adapter pattern (?), I would like to create a list of objects that includes persons from DB1 and DB2 under the same type - of PersonInOurDB.
In the end, my goal is to be able to call GSON serialization/desarialization on myClass alone.
public class PersonInOurDB {
private String firstname;
private String lastname;
}
A simple selection based on the type is all you really need. This could be considered a builder pattern because it just initializes a new instance of myClass.
Note, this is rough pseudo code.
FunctionName(SomeType instance)
{
string aPostfix = "_1";
string bPostfix = "_2";
string selectedPostFix;
// This is your strategy selector
switch(typeof(SomeType.Name)
{
case "TypeA":
selectedPostFix = aPostFix;
case "TypeB":
selectedPostFix = bPostFix;
}
return new myClass()
{
A = instance.GetProperty("A" + selectedPostfix).Value,
B = instance.GetProperty("B" + selectedPostfix).Value,
...
}
}
If you want a common access api in java for both objects, then introduce an interface and let both implement it.
If you only want both objects (PersonFromDB1 and PersonFromDB2) to be serialized in the same way by json you can either:
use annotations - the #SerializedName annotation in combination with #Expose.
use the FieldNamingStratgy and ExclusionStrategy
Use annotations to control the serialization
public class PersonFromDB1 {
#Expose
#SerializedName("firstName")
private String firstName;
#Expose
#SerializedName("lastName")
private String lastName;
private String car;
}
public class PersonFromDB2 {
#Expose
#SerializedName("firstName")
private String first_Name;
#Expose
#SerializedName("lastName")
private String last_Name;
private String boat;
}
Then you can use the GsonBuilder
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
PersonFromDB1 person1 = ...; // get the object
PersonFromDB2 person2 = ...; // get the object
System.out.println(gson.toJson(person1));
System.out.println(gson.toJson(person2));
Use FieldNamingStratgy and ExclusionStrategy to control the serialization
If you don't want to modify the db objects (you can't or you don't want to add annotations) than there is another way. You can use a FieldNamingStratgy and ExclusionStrategy.
class PersonFromDBNamingStrategy implements FieldNamingStrategy {
Map<String, String> fieldMapping = new HashMap<String, String>();
public PersonFromDBNamingStrategy() {
fieldMapping.put("first_Name", "firstName");
fieldMapping.put("last_Name", "lastName");
}
#Override
public String translateName(Field f) {
String name = f.getName();
if(fieldMapping.contains(name)){
return fieldMapping.get(name);
}
return name;
}
}
and the ExclusionStrategy
class PersonFromDExclusionStrategy implements ExclusionStrategy {
List<String> validNames = Arrays.asList("car", "boat");
#Override
public boolean shouldSkipField(FieldAttributes f) {
String name = f.getName();
return !validNames.contains(name);
}
#Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
}
after that just create Gson like this:
GsonBuilder gsonBuilder = new GsonBuilder();
sonBuilder.addSerializationExclusionStrategy(new PersonFromDExclusionStrategy());
gsonBuilder.setFieldNamingStrategy(new PersonFromDBNamingStrategy());
Gson gson = gsonBuilder.create();
PersonFromDB1 person1 = ...; // get the object
PersonFromDB2 person2 = ...; // get the object
System.out.println(gson.toJson(person1));
System.out.println(gson.toJson(person2));
I have created a class Game that looks like this:
public class Game{
private String name;
private String location;
private int participants;
public Game(String name, String location, int participants){
this.name = name;
this.location = location;
this. participants = participatns;
}
//getters and setters of all properties
And I have a ArrayList that looks like this:
ArrayList<Game>gameList = new ArrayList<Game>();
This ArrayList contains 5 games. I want to send those games to a php script so I figured that the smartest way to do this is to create a JSON array of objects and send those to the php script because those games could also be a 100 or more at some point.
My question is, how do I create a JSON array of Game objects?
Use Gson: https://sites.google.com/site/gson/gson-user-guide#TOC-Using-Gson
Game game = new Game();
Gson gson = new Gson();
String json = gson.toJson(game);
The best thing to do is get into GSON. GSON web site,
public class ExampleObject {
public static final String API_KEY = "get_response";
private static final String OFFSETS = "offsets";
#SerializedName(OFFSETS)
private List<Integer> mOffsets;
public ExampleObject() {
super();
}
public String getAPIKey() {
return API_KEY;
}
public List<Integer> getOffsets() {
return mOffsets;
}
}
I'm working on an Android project and I'm trying to make use of an ArrayList which is of type MyClass. I am trying to store data in the ArrayList to each of the variables within MyClass. Below is the code I am using.
Below is the Class with the variables that will be used.
class SearchData
{
public int id;
public String category;
public String company;
public String loginAction;
public String username;
public String password;
public String type;
public String appName;
}
Below is how I am initialising the ArrayList
ArrayList<SearchData> passwords = new ArrayList<SearchData>();
And below is how I am trying to add new data to the ArrayList
passwords.add(new SearchData()
{
});
I can't figure out how to then set the variables from within the class with the data that I need them to be set to. In C#, which I know more about than Java, I can do the following:
passwords.add(new SearchData()
{
id = 0,
category = "hello"
});
However, I'm not seeing any of the variables that are within the class being shown in the Intellisense help.
What am I doing wrong?
You need to create an object and set all the attributes first, and then add it to the List: -
SearchData searchData = new SearchData();
searchData.setId(1);
searchData.setCategory(category);
...
passwords.add(searchData);
Create a constructor for your class.
class SearchData
{
public int id;
public String category;
public String company;
public String loginAction;
public String username;
public String password;
public String type;
public String appName;
SearchData(int id, String category, String company......){
this.id = id;
this.category = category;
this.company = company;
...
}
}
Then use it like this:
passwords.add(new SearchData(0,"Category1", "Company1"......));
Create an object and store reference to it:
SeachData searchData = new SearchData();
Set the properties you want to set:
searchData.setId(123);
...so on
searchData. Ctrl+Space should show the intellisense now..
Adding the search reference to the list:
list.add(searchData);
SearchData sd = new SearchData();
sd.id = 0;
sd.category = "hello";
passwords.add(sd);