Gson : parsing json string to java arraylist like modeling - gson

I tried to parse a Json response I guet from my limesurvey server to Java, however I don't see what type of Class I should create it.
Here is the json string that I want to parse into a java object :
{"id":1,"result":{"gid":"1","type":"L","help":"Veuillez entrer votre niveau d'\u00e9tudes \u00e0 l'INPT.","language":"fr","sid":"796246","question_order":"3","question":"<p>\r\n\tvotre niveau d'\u00e9tudes \u00e0 l'INPT :<\/p>\r\n","answeroptions":{"A1":{"answer":"INE1","assessment_value":"0","scale_id":"0"},"A2":{"answer":"INE2","assessment_value":"1","scale_id":"0"},"A3":{"answer":"INE3","assessment_value":"1","scale_id":"0"}}},"error":null}
In order to parse it I created the following classes :
public class Answer
{
String answer;
int assessment_value;
int scale_id;
#Override
public String toString() {
return "answer : "+answer + " - assessment_value : " + assessment_value + " - scale_id :" + scale_id;
}
}
public class answerOptions
{
String a;
Answer t;
}
public class QuestionProperties
{
int gid;
String type;
String help;
String language;
int sid;
int question_order;
String question;
ArrayList<answerOptions> answeroptions;
#Override
public String toString() {
return "gid : "+gid + " - type : " + type + " - help :" + help + " - language :" + language + " - sid :" + sid + " - question_order :" + question_order + " - question :" + question;
}
}
public class getQuestionProperties
{
int id;
QuestionProperties result;
String error;
#Override
public String toString() {
return "id : "+id + " - result : " + result + " - error :" + error;
}
}
The problem I have is that I declared "answeroptions" as an array while it is not, however I can't know for sure the number of options in there, I tried a class that works like a pair of a sort but it's not working ?? any ideas on how to model this problem without loosing genrality since I want a method to automatically parse this whatever the number of options !!

You are almost doing well. Just change it to Map<String,Answer> instead of ArrayList<answerOptions> as per the JSON string. Here "A1", "A2" and "A3" are keys of the Map.
sample:
class QuestionProperties {
...
Map<String,Answer> answeroptions;
}
In JSON String anything enclosing inside
{...} is converted to Map or custom POJO class Object
[...] is converted to ArrayList
Learn more... about JSON and follow this post
Note: Follow Java Naming convention for class name. Encapsulate all variables by making private and use proper getter/setter methods.

Related

How to validate path parameter in microprofile

Here key is pathParam. And even if I send the value of key greater than 9. It sends me valid response, instead of throwing validation error.
Curl request -
curl --location --request GET 'localhost:8080/v1/batch-tokens/11/action/count'
Code -
#Path("/v1/batch-tokens")
public class BatchTokenResource implements Serializable {
private static final Logger LOGGER = Logger.getLogger(BatchTokenResource.class.getName());
private static final String LOG_HEADER = "[" + BatchTokenResource.class.getSimpleName() + "]::";
#GET
#Path("/{key}/action/count")
public Response countTokenPerKey(
#PathParam("key") #Min(value = 0) #Max(value = 9)
#Pattern(regexp = "^\\d$") String key
) {
LOGGER.log(Level.INFO, () -> LOG_HEADER + "countTokenPerKey " +
"key=" + key
);
try {
Long tokenCount = 100L;
return Response.ok(new TokenModel(key, tokenCount)).build();
} catch (Exception ex) {
LOGGER.log(Level.WARNING, () -> LOG_HEADER + "countTokenPerKey failed." +
"key=" + key +
"exception=" + ex.getMessage()
);
throw ex;
}
}
}
I tried searching on quarkus.io/guide and internet, but only found docs to validate bean parameters.
Guide for validating bean parameters:
https://quarkus.io/guides/validation
https://microprofile.io/2019/07/11/validate-your-microservices-with-microprofile-and-bean-validation
Looks like the culprit is the type of the parameter:
String key
https://javadoc.io/doc/jakarta.validation/jakarta.validation-api/latest/jakarta/validation/constraints/Max.html
Supported types are:
BigDecimal
BigInteger
byte, short, int, long, and their respective wrappers
Note that double and float are not supported due to rounding errors (some providers might provide some approximative support).

How to get entity relationship with annotations using testing class?

I have created a simple Spring-boot application with two entities as Company.java and User.java. These two has #OneToMany relationship. And I have a created a test file for generating typescript file with printing those two entity's attributes. Here is the my test case.
#Inject
RepositoryRestMvcConfiguration configuration;
#Test
public void getEndPoints() {
configuration.resourceMappings().forEach(c -> {
String className = c.getDomainType().getName();
try {
Class<?> entityClass = Class.forName(className);
Field[] fields = entityClass.getDeclaredFields();
File tsClassDir = new File("data/tsClass");
File tsClass = new File(tsClassDir, entityClass.getSimpleName() + ".ts");
if (!tsClass.getParentFile().exists()) {
tsClass.getParentFile().mkdirs();
}
tsClass.createNewFile();
String code = "export interface " + entityClass.getSimpleName() + "{\n";
for (Field field : fields) {
try {
NotNull notNullAnnotation = field.getDeclaredAnnotation(NotNull.class);
Class<?> filedClass = Class.forName(field.getType().getName());
if (notNullAnnotation == null){
code += "\t" + field.getName() + "?: " + filedClass.getSimpleName().trim() + ";" + "\n";
}else{
code += "\t" + field.getName() + ": " + filedClass.getSimpleName().trim() + ";" + "\n";
}
} catch (Exception e) {
// TODO: handle exception
}
// System.err.println(field.getName());
}
code += "}";
Files.write(tsClass.toPath(), code.getBytes());
System.err.println(code);
} catch (Exception e) {
// TODO: handle exception
}
});
}
After test run I got the result given below.
export interface User{
userName: String;
password: String;
email: String;
company?: Company;
}
export interface Company{
name: String;
email: String;
users?: Set;
}
But I need to print that Company and User has #OneToMany relationship in the typescript file. How do I do that?

BsonClassMapSerializer already registered for AbstractClassSerializer

I'm using the Mongo c# driver 2.0 and am running into BsonSerializer registration issues when registering AbstractClassSerializers for my Id value objects.
MongoDB.Bson.BsonSerializationException: There is already a serializer registered for type HistoricalEventId.
When I peek into the BsonSerializer I'm seeing that a BsonClassMapSerializer is already registered for my type.
I'm assuming that a BsonClassMapSerializer is being created for my entity types and it's also creating a BsonClassMapSerializer for the Id field as well. Has anyone run into this before? The Bson serializer code is shown below if that helps.
Sorry if the formatting is wrong, c# doesn't seem to be showing up well.
HistoricalEventIdBsonSerializer
public class HistoricalEventIdBsonSerializer : ToObjectIdBsonSerializer<HistoricalEventId>
{
public override HistoricalEventId CreateObjectFromObjectId(ObjectId serializedObj)
{
HistoricalEventId parsedObj;
HistoricalEventId.TryParse(serializedObj, out parsedObj);
return parsedObj;
}
}
ToObjectIdBsonSerializer
public abstract class ToObjectIdBsonSerializer<T> : AbstractClassSerializer<T> where T : class
{
private static readonly Type _convertibleType = typeof(IConvertible<ObjectId>);
public abstract T CreateObjectFromObjectId(ObjectId serializedObj);
public override T Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
var bsonType = context.Reader.GetCurrentBsonType();
ObjectId value;
switch (bsonType)
{
case BsonType.Undefined:
value = ObjectId.Empty;
context.Reader.ReadUndefined();
break;
case BsonType.Null:
value = ObjectId.Empty;
context.Reader.ReadNull();
break;
case BsonType.ObjectId:
value = context.Reader.ReadObjectId();
break;
case BsonType.String:
value = new ObjectId(context.Reader.ReadString());
break;
default:
throw new NotSupportedException("Unable to create the type " +
args.NominalType.Name + " from the bson type " + bsonType + ".");
}
return this.CreateObjectFromObjectId(value);
}
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, T value)
{
if (value == null)
{
context.Writer.WriteObjectId(ObjectId.Empty);
}
else
{
if (!_convertibleType.IsAssignableFrom(args.NominalType))
{
throw new NotSupportedException("The type " + args.NominalType.Name +
" must implement the " + _convertibleType.Name + " interface.");
}
var typedObj = (IConvertible<ObjectId>)value;
context.Writer.WriteObjectId(typedObj.ToValueType());
}
}
}
IConvertible
public interface IConvertible<out T>
{
T ToValueType();
}
My assumption must have been correct because I just fixed this by doing the BsonSerializer registration before creating the MongoClient and getting the database. Hopefully this will help someone else.

I am having a lot of trouble finding the right way to sort by price

I'm pretty sure that I have to use Collections, but I'm honestly super lost on how to implement it! My assignment needs me to
"Allow the user to view the plants sorted by price (lowest to highest), scientific name (alphabetized by genus), or common name (alphabetized by first letter of first word)."
this is my code, I don't know where to exactly put the sorting and how to write it in code :(
package plant.nursery.program;
import java.util.Scanner;
import java.util.ArrayList;
public class PlantNurseryProgram {
private double maxHeightInFt;
private String commonName;
private String scientificName;
private double price;
boolean isFragile;
public PlantNurseryProgram(String commonName,
String scientificName, double maxHeightInFt, double price,
boolean isFragile) {
this.maxHeightInFt = maxHeightInFt;
this.commonName = commonName;
this.scientificName = scientificName;
this.price = price;
this.isFragile = isFragile;
}
#Override
public String toString() {
return commonName + " (" + scientificName + ") " + "is " +
maxHeightInFt + " ft tall " + "it costs $" + price + " and "
+ "isFragile = " + isFragile;
}
public String setName(String newName){
commonName = newName;
return newName;
}
public String setSName(String newSName)
{
scientificName = newSName;
return scientificName;
}
public double setHeight(double newHeight){
maxHeightInFt = newHeight;
return maxHeightInFt;
}
public double setPrice(double newPrice){
price = newPrice;
return price;
}
public boolean setFragile(boolean newFragile){
isFragile = newFragile;
return isFragile;
}
public static void main(String[] args) {
Scanner plant = new Scanner(System.in);
boolean toContinue = true;
ArrayList<PlantNurseryProgram> plantNurseryAL = new ArrayList<>();
do
{
System.out.println("What's the name of your plant");
String commonName = plant.next();
System.out.println("What's the scientific name for your plant?");
String scientificName = plant.next();
System.out.println("How tall is your plant?");
double maxHeightInFt = plant.nextDouble();
System.out.println("What's the price of your plant?");
double price = plant.nextDouble();
System.out.println("Is the plant fragile? If yes type(true), if no "
+ "type (false)");
boolean isFragile = plant.nextBoolean();
System.out.println("Do you wish to continue?");
toContinue = plant.nextBoolean();
PlantNurseryProgram userPlant = new PlantNurseryProgram(
commonName, scientificName, maxHeightInFt, price, isFragile);
plantNurseryAL.add(userPlant);
System.out.println("Is all the information you entered correct?");
boolean corrections = plant.nextBoolean();
if(corrections == false)
{
System.out.println("What would you like to correct?"
+ " 1. Name, 2. Scientific name, 3. Height, 4. Price"
+ "5. Fragility?" );
int userChoice = plant.nextInt();
if(userChoice == 1)
{
System.out.println("Please enter the correct name");
String newName = plant.next();
userPlant.setName(newName);
System.out.println(userPlant);
}
else if(userChoice == 2)
{
System.out.println("Please enter the correct scientific name");
String newSName = plant.next();
userPlant.setSName(newSName);
System.out.println(userPlant);
}
else if(userChoice == 3)
{
System.out.println("Please enter the correct height");
double newHeight = plant.nextDouble();
userPlant.setHeight(newHeight);
System.out.println(userPlant);
}
else if(userChoice == 4)
{
System.out.println("Please enter the correct price");
double newPrice = plant.nextDouble();
userPlant.setPrice(newPrice);
System.out.println(userPlant);
}
else if(userChoice == 5)
{
System.out.println("Please enter if the plant is fragile or not");
boolean newFragile = plant.nextBoolean();
userPlant.setFragile(newFragile);
System.out.println(userPlant);
}
}
} while(toContinue == true);
for (PlantNurseryProgram plantNurseryProgram : plantNurseryAL) {
System.out.println(plantNurseryProgram);
} // end of for loop
} //end of main
} // end of class
You're posting too much here. To figure out what to do, you don't need a main method or a user interface. By the way, conventionally, setters are void methods.
You should become familiar with the available methods for collections in the java.util package. Or you can use the index and look up sort. You will see that the Collections class has two sort methods. One is for the case when the class is inherently Comparable:
public class TemperatureGauge implements Comparable<TemperatureGauge> {...}
The other is when a class may be sorted many different ways, so that there is no natural way to define a standard comparison. Then, you create a Comparator.
public class CommonNameComparator implements Comparator<PlantNurseryProgram> {
public int compare(PlantNurseryProgram left, PlantNurseryProgram right) {
// Do something with left.getCommonName() and right.getCommonName().
// It must return a negative, zero, or positive depending on whether
// left should come before, in the same place, or after right.
return /*anInteger*/;
}
}
Repeat the process for the scientific name and the price.
For extra credit, write JUnit tests for each comparator and for the sorting process.
You can use set interface of collection class.Before that first you should override the equals and hashcode method in your code with the field according to which you want to short.
After this you can create object of PlantNurseryProgram and this in one of the subclass of set interface.As per your requirement you can use Treeset class.
Reemember there are several other ways to ddo this.Using set you can not duplicate item.
If your purpose is to sort after inserting the data in data structure the use sort method provided in java collection api.
See the below link.
http://www.roseindia.net/java/jdk6/set-interface.shtml

Selecting From Dynamic Attribute ASP.NET MVC3

I'm developing an ASP.NET MVC3 project which has multi-language support. I hold all the words in database and select from it according to a Session value. At present I get the value as the following:
public ActionResult Index()
{
string lang = (string)System.Web.HttpContext.Current.Session["Language"];
if (lang == "Tr" || lang == null)
{
ViewBag.Admin = db.Words.Find(10).Tr;
}
else if(lang == "En")
{
ViewBag.Admin = db.Words.Find(10).En;
}
return View();
}
The present type of Word:
public class Word
{
public int ID { get; set; }
public string Tr { get; set; }
public string En { get; set; }
}
However, the number of languages will raise, and with the present method I need to add these by hand. What I want is getting the word's "Session" language form dynamically such as (in pseudo):
ViewBag.Admin = db.Words.Find(10)."Session[Language]";
I know it should be easy but couldn't find the appropriate keywords to find this. Any idea how can I achieve this?
EDIT: I just need a way to execute an SQL String like the following:
String lang = (string)System.Web.HttpContext.Current.Session["Language"];
ViewBag.MyWord = ExecuteSQL.("SELECT " + lang + " FROM Words WHERE ID = " + 10 + " ");
EDIT 2: I tried the following lines:
ViewBag.Admin = db.Words.SqlQuery("SELECT" +
(string)System.Web.HttpContext.Current.Session["Language"] +
"FROM Words WHERE ID=10");
However, the output for this is the query itself on the screen. (SELECT Tr FROM Words WHERE ID=10)
If you are using EF as your ORM you could do something like this:
int wordId = 10;
string lang = (string)System.Web.HttpContext.Current.Session["Language"];
var query = "SELECT " + lang + " FROM Words WHERE ID = {0}";
var word = context.Database.SqlQuery<string>(query, wordId).FirstOrDefault();
See here for more info on raw sql queries:
http://msdn.microsoft.com/en-us/data/jj592907.aspx
NOTE: The above could be open to sql injection, probably best to test that it's a 2 character language code before sending the sql request to the server.
You could do it by reflection. Just get the property by the language-key.
Here is some code:
Word word = ...
string lang = (string)System.Web.HttpContext.Current.Session["Language"];
PropertyInfo pi = typeof(Word).GetProperty(lang);
return pi.GetValue(word, null);
I did not at exception handling.
You could use an expression/delegate.
Write a methode returning the expression depending on Session["Language"].
private string GetLanguageStringByKey(int key){
return GetFuncByLanguage()(db.Words.Find(key));
}
private Func<Word,string> GetFuncByLanguage(){
string lang = (string)System.Web.HttpContext.Current.Session["Language"];
Func<Word, string> func;
switch(lang){
case "En":{
func = w => w.En;
break;
}
default:{
func = w => w.Tr;
break;
}
}
return func;
}
public ActionResult Index()
{
ViewBag.Admin = GetLanguageStringByKey(10);
return View();
}

Resources