How to add an object without a constructor to a list using Spring Expression Language - spring-el

I want to add a BigDecimal to a list using Spring Expression Language.
public class SpelTest {
public List<BigDecimal> values;
StandardEvaluationContext context;
SpelExpressionParser parser;
#Before
public void setup() {
values = new ArrayList<>();
context = new StandardEvaluationContext(this);
parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
}
#Test
public void shouldChangeValue() {
values.add(BigDecimal.ONE);
parser.parseExpression("values[0]").setValue(context, "123.4");
assertThat(values.get(0)).isEqualTo(BigDecimal.valueOf(123.4)); // passes
}
#Test
public void shouldAddValue() {
parser.parseExpression("values[0]").setValue(context, "123.4");
assertThat(values.get(0)).isEqualTo(BigDecimal.valueOf(123.4)); // fails
}
}
Changing the first entry passes but adding an entry fails with
Caused by: java.lang.NoSuchMethodException: java.math.BigDecimal.<init>()
at java.base/java.lang.Class.getConstructor0(Class.java:3349)
at java.base/java.lang.Class.getDeclaredConstructor(Class.java:2553)
at org.springframework.util.ReflectionUtils.accessibleConstructor(ReflectionUtils.java:185)
at org.springframework.expression.spel.ast.Indexer$CollectionIndexingValueRef.growCollectionIfNecessary(Indexer.java:715)
... 55 more
Not sure why SpEL isn't able to properly initialize a BigDecimal when the list is empty. Surprisingly I found nothing about this problem.
Thanks for helping!

The problem is that you activated autoGrowCollections on the SpelParserConfiguraion.
Therefore it tries to create an element with a default constructor if you try to access a non-existing element of the collection with the index operator []. BigDecimal has no default constructor and because of this, it fails.
What you could do is to create the object in the SpEL itself. E.G.:
#Test
public void shouldAddValue() {
parser.parseExpression("values.add(0, new java.math.BigDecimal(\"123.4\"))").getValue(context);
assertThat(values.size() > 0);
assertThat(values.get(0)).isEqualTo(BigDecimal.valueOf(123.4));
}
Or you could create a subclass of BigDecimal witch has a default constructor and use this class.

You could avoid this problem by setting the whole list instead of single (not initialized) elements. Instead of
parser.parseExpression("values[0]").setValue(context, "123.4");
use:
parser.parseExpression("values").setValue(context, "123.4");
This also works for multiple elements, quite neat:
parser.parseExpression("values").setValue(context, "123.4, 456.7");

As #Nirud pointed out, the problem is, that BigDecimal does not have a default constructor. I extended SpEL to add null to the list, when there is no default constructor. See this pull request: https://github.com/spring-projects/spring-framework/pull/25367

Related

Hashset to string [duplicate]

I have a class defined as follows:
public class Person {
private String name;
// constructor and getter/setter omitted
}
I tried to print an instance of my class:
System.out.println(myPerson);
but I got the following output: com.foo.Person#2f92e0f4.
A similar thing happened when I tried to print an array of Person objects:
Person[] people = //...
System.out.println(people);
I got the output: [Lcom.foo.Person;#28a418fc
What does this output mean? How do I change this output so it contains the name of my person? And how do I print collections of my objects?
Note: this is intended as a canonical Q&A about this subject.
Background
All Java objects have a toString() method, which is invoked when you try to print the object.
System.out.println(myObject); // invokes myObject.toString()
This method is defined in the Object class (the superclass of all Java objects). The Object.toString() method returns a fairly ugly looking string, composed of the name of the class, an # symbol and the hashcode of the object in hexadecimal. The code for this looks like:
// Code of Object.toString()
public String toString() {
return getClass().getName() + "#" + Integer.toHexString(hashCode());
}
A result such as com.foo.MyType#2f92e0f4 can therefore be explained as:
com.foo.MyType - the name of the class, i.e. the class is MyType in the package com.foo.
# - joins the string together
2f92e0f4 the hashcode of the object.
The name of array classes look a little different, which is explained well in the Javadocs for Class.getName(). For instance, [Ljava.lang.String means:
[ - an single-dimensional array (as opposed to [[ or [[[ etc.)
L - the array contains a class or interface
java.lang.String - the type of objects in the array
Customizing the Output
To print something different when you call System.out.println(myObject), you must override the toString() method in your own class. Here's a simple example:
public class Person {
private String name;
// constructors and other methods omitted
#Override
public String toString() {
return name;
}
}
Now if we print a Person, we see their name rather than com.foo.Person#12345678.
Bear in mind that toString() is just one way for an object to be converted to a string. Typically this output should fully describe your object in a clear and concise manner. A better toString() for our Person class might be:
#Override
public String toString() {
return getClass().getSimpleName() + "[name=" + name + "]";
}
Which would print, e.g., Person[name=Henry]. That's a really useful piece of data for debugging/testing.
If you want to focus on just one aspect of your object or include a lot of jazzy formatting, you might be better to define a separate method instead, e.g. String toElegantReport() {...}.
Auto-generating the Output
Many IDEs offer support for auto-generating a toString() method, based on the fields in the class. See docs for Eclipse and IntelliJ, for example.
Several popular Java libraries offer this feature as well. Some examples include:
ToStringBuilder from Apache Commons Lang
MoreObjects.ToStringHelper from Google Guava
#ToString annotation from Project Lombok
Printing groups of objects
So you've created a nice toString() for your class. What happens if that class is placed into an array or a collection?
Arrays
If you have an array of objects, you can call Arrays.toString() to produce a simple representation of the contents of the array. For instance, consider this array of Person objects:
Person[] people = { new Person("Fred"), new Person("Mike") };
System.out.println(Arrays.toString(people));
// Prints: [Fred, Mike]
Note: this is a call to a static method called toString() in the Arrays class, which is different to what we've been discussing above.
If you have a multi-dimensional array, you can use Arrays.deepToString() to achieve the same sort of output.
Collections
Most collections will produce a pretty output based on calling .toString() on every element.
List<Person> people = new ArrayList<>();
people.add(new Person("Alice"));
people.add(new Person("Bob"));
System.out.println(people);
// Prints [Alice, Bob]
So you just need to ensure your list elements define a nice toString() as discussed above.
I think apache provides a better util class which provides a function to get the string
ReflectionToStringBuilder.toString(object)
Every class in Java has the toString() method in it by default, which is called if you pass some object of that class to System.out.println(). By default, this call returns the className#hashcode of that object.
{
SomeClass sc = new SomeClass();
// Class # followed by hashcode of object in Hexadecimal
System.out.println(sc);
}
You can override the toString method of a class to get different output. See this example
class A {
String s = "I am just a object";
#Override
public String toString()
{
return s;
}
}
class B {
public static void main(String args[])
{
A obj = new A();
System.out.println(obj);
}
}
In Eclipse,
Go to your class,
Right click->source->Generate toString();
It will override the toString() method and will print the object of that class.
I prefer to use a utility function which uses GSON to de-serialize the Java object into JSON string.
/**
* This class provides basic/common functionalities to be applied on Java Objects.
*/
public final class ObjectUtils {
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private ObjectUtils() {
throw new UnsupportedOperationException("Instantiation of this class is not permitted in case you are using reflection.");
}
/**
* This method is responsible for de-serializing the Java Object into Json String.
*
* #param object Object to be de-serialized.
* #return String
*/
public static String deserializeObjectToString(final Object object) {
return GSON.toJson(object);
}
}
In intellij you can auto generate toString method by pressing alt+inset and then selecting toString() here is an out put for a test class:
public class test {
int a;
char b;
String c;
Test2 test2;
#Override
public String toString() {
return "test{" +
"a=" + a +
", b=" + b +
", c='" + c + '\'' +
", test2=" + test2 +
'}';
}
}
As you can see, it generates a String by concatenating, several attributes of the class, for primitives it will print their values and for reference types it will use their class type (in this case to string method of Test2).
By default, every Object in Java has the toString() method which outputs the ObjectType#HashCode.
If you want more meaningfull information then you need to override the toString() method in your class.
public class Person {
private String name;
// constructor and getter/setter omitted
// overridding toString() to print name
public String toString(){
return name;
}
}
Now when you print the person object using System.out.prtinln(personObj); it will print the name of the person instead of the classname and hashcode.
In your second case when you are trying to print the array, it prints [Lcom.foo.Person;#28a418fc the Array type and it's hashcode.
If you want to print the person names, there are many ways.
You could write your own function that iterates each person and prints
void printPersonArray(Person[] persons){
for(Person person: persons){
System.out.println(person);
}
}
You could print it using Arrays.toString(). This seems the simplest to me.
System.out.println(Arrays.toString(persons));
System.out.println(Arrays.deepToString(persons)); // for nested arrays
You could print it the java 8 way (using streams and method reference).
Arrays.stream(persons).forEach(System.out::println);
There might be other ways as well. Hope this helps. :)
If you Directly print any object of Person It will the ClassName#HashCode to the Code.
in your case com.foo.Person#2f92e0f4 is getting printed . Where Person is a class to which object belongs and 2f92e0f4 is hashCode of the Object.
public class Person {
private String name;
public Person(String name){
this.name = name;
}
// getter/setter omitted
#override
public String toString(){
return name;
}
}
Now if you try to Use the object of Person then it will print the name
Class Test
{
public static void main(String... args){
Person obj = new Person("YourName");
System.out.println(obj.toString());
}
}
If you look at the Object class (Parent class of all classes in Java) the toString() method implementation is
public String toString() {
return getClass().getName() + "#" + Integer.toHexString(hashCode());
}
whenever you print any object in Java then toString() will be call. Now it's up to you if you override toString() then your method will call other Object class method call.
Using Lombok #Data annotation on class will provide getter, setter, toString and hashcode. Using Lombok is better as it handles boilerplate code.
For a "deep" toString() there is an alternative to the JSON based answers (Jackson, GSON, etc.): ReflectionToStringBuilder from the Apache Commons Lang 3 library, with RecursiveToStringStyle or MultilineRecursiveToStringStyle. Code example:
System.out.println("My object: " +
ReflectionToStringBuilder.toString(theObject, new RecursiveToStringStyle()));
Output examples:
// RecursiveToStringStyle
Person#7f54[name=Stephen,age=29,smoker=false,job=Job#43cd2[title=Manager]]
// MultilineRecursiveToStringStyle
Person#7f54[
name=Stephen,
age=29,
smoker=false,
job=Job#43cd2[
title=Manager
]
]
I managed to get this done using Jackson in Spring 5. Depending on the object it might not work in all cases.
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(yourObject));
the output would look like
{
"id" : 1,
"fieldOne" : "string"
}
Here are more examples using Jackson
If you use GSON instead It might look like
Gson gson = new Gson();
System.out.println(gson.toJson(yourObject));
If you are using project Lombok you could use the #ToString annotation and generate a standard toString() method without adding boilerplate.
import lombok.ToString;
#ToString
public class LoginDto {
private String user;
private String pass;
}
...
System.out.println(loginDto.toString());
// LoginDto(user=x#xxx.x, pass=xxxxx)

How to initialize an array object

Why I am getting the error while runtime:
Exception in thread "main" java.lang.NullPointerException at arrayTest.main(arrayTest.java:5)
Source code is as following:
public class arrayTest{
int b;
public static void main(String[] args){
arrayTest[] a= new arrayTest[2];
a[0].b=10;
System.out.println(a[0].b);
}
}
Thanks
Sunil
You haven't added any arrayTest instances to your array. You've only initialized the array, so arrayTest[0] is null. To add objects to the array:
arrayTest[0] = new arrayTest();
arrayTest[1] = new arrayTest();
When doing Object-Oriented-Programming (OOP) you need to think a bit differently.
Here what you are doing is creating a new object arrayTest from the class arrayTest.
This object need a constructor and fields which are properties so it can be well defined for instance: size, age or eyes colour for a person.
Here what do you want your object to be?
For instance, I want to do an ArrayList of n object of the same type :
First, my field is going to be an ArrayList because that's what I want to create.
I have the following class :
import java.lang.reflect.Type;
import java.util.ArrayList;
class arrayTestType {
private final ArrayList arrayTest;
}
What should my constructor return?
Well, an array of type Type with n elements is ArrayList<Type>(n)
so my constructor should be written like this :
public arrayTestType(Type type, int length) {
arrayTest = new ArrayList<Type>(length);
}
So finally our class is :
import java.lang.reflect.Type;
import java.util.ArrayList;
class arrayTestType {
private final ArrayList arrayTest;
public arrayTestType(Type type, int length){
arrayTest = new ArrayList<Type>(length);
}
}
Now when we are going to call :
arrayTestType stringArrayOfLength5 = new arrayTestType<String>(5);
We are going to get an object well defined.
Now you need to add things in order for it not to be null hence the null pointer !
Good luck !

Need to verify size of an array in unit testing [duplicate]

This question already has answers here:
How do I test a class that has private methods, fields or inner classes?
(58 answers)
Closed 8 years ago.
Class Elem{
private ArrayList<someType> arr = new ArrayList<>();
public void addElement(someType var) {
arr.add(var);
}
public someType bestelement() {
someType first= arr.get(0);
arr.remove(0);
return first;
}
}
I have written test case for this method and it's running successfully but I need to know how can I be sure that the remove() method was called and the size of array list was reduced by 1? Please do tell me how to get the size of arr in my test case?
Test case for this method
Class ElemTest{
private Elem obj = new Elem();
private someType var1;
private someType var2;
private ArrayList<someType> testArr = new ArrayList<>();
#Test
public void bestElementTest() {
obj.addElement(var1);
obj.addElement(var2);
testArr.add(var1);
testArr.add(var2);
someType result = testArr.get(0);
assertEquals("test failed", result, obj.bestElem());
}
}
Given your example its difficult to advise you, but in general terms you options are to set the class into a known state, call you method which chnages the class and then check that the state has changed in the way you expect.
In this specific case if you have access to the internal list somewhere then you can check that the list has changed (ie the element has been removed). If you don't have access to the internal list then you need to check other things, like:
if you call bestElement() again when the object contains more than 1 element you get a different element
if you call bestElement() again when the object should not have any more elements you get an exception
These obviously rely on being able to set the object into one of the above states and we don't know if that is possible from the sample.
My final piece of advise would be to focus the test on the behaviour that you want to see from your class, not in the implementation of how the class works.

Autofixture, expected behavior?

Having a test similar to this:
public class myClass
{
public int speed100index = 0;
private List<int> values = new List<int> { 200 };
public int Speed100
{
get
{
return values[speed100index];
}
}
}
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
var fixture = new Fixture();
var sut = fixture.Create<myClass>();
Assert.AreEqual(sut.Speed100, 200);
}
}
Would have expected this to work, but I can see why it's not. But how do I argue, that this is not a problem with AutoFixture, but a problem with the code?
AutoFixture is giving you feedback about the design of your class. The feedback is, you should follow a more object-oriented design for this class.
Protect your private state, to prevent your class from entering an inconsistent state.
You need to make the speed100index field, private, to ensure it remains consistent with the values List.
Here is what I see if I run debugger on your test:
Autofixture assigns a random number to speed100index field because it is public, and in your array there is nothing at point 53 (from my screenshot)
If you set speed100index to be private, Autofixture will not re-assign the number and your test will pass.

Collection is empty when returning from JAX-RS/CXF service

I have a service method defined as:
public JaxbList<Deal> getDeal() {
List<Deal> deals = new ArrayList<Deal>();
Deal type = new Deal();
type.setDealID(1);
type.setName("June Discounts");
deals.add(type);
JaxbList list = new JaxbList(deals);
System.out.println("List size -> " + list.getList().size());
return list;
}
My client is defined as:
WebClient client = WebClient.create("....");
JaxbList deals = client.path("exampleservice/getDeal")
.accept("application/xml").get(JaxbList.class);
List<Deal> types = deals.getList();
When I print out the size of the collection in the service method, the result comes back as 1. But, the size of my 'types' list from the client is 0. When I open in a browser, the 1 deal is displayed. So, this issue seems to be my client. I'm not sure where though.
Ideas?
Here is my JaxbList class:
public class JaxbList<T>{
protected List<T> list;
public JaxbList(){}
public JaxbList(List<T> list){
System.out.println("Setting list...");
this.list=list;
}
#XmlElement(name="Item")
public List<T> getList(){
return list;
}
}
As mentioned above by KasunBG your public JaxbList(List<T> list) constructor is never called by JAXB. The default no-arg one is used instead (see some discussion at What JAXB needs a public no-arg constructor for?). Actually Java compiler should complain about this situation with two constructors and "never initialized field list".
The solution is to introduce a setList() setter and throw a runtime exception from a no-arg constructor.

Resources