I have the following class:
public static class TestSomething {
Integer test;
public TestSomething(Integer test) {
this.test = test;
}
// getter and setter for test
}
Ok, now create a collection of this class and serialize it with Gson:
Collection<TestSomething> tests = Arrays.asList(
new TestSomething(1),
new TestSomething(2),
new TestSomething(3)
);
String json = new Gson().toJson(tests, new TypeToken<Collection<TestSomething>>() {}.getType());
After this, the String json is set to
[{"test":1},{"test":2},{"test":3}]
Which is great.
But now, all of my model classes inherit from a generic type Identifiable<T> which provides just two methods T getId() and void setId(T). So I change the TestSomething-class from above to
public static class TestSomething extends Identifiable<Long> {
// same as above
}
When I try to put this through Gson.toJson(), Gson ends up with the following Exception:
java.lang.UnsupportedOperationException: Expecting parameterized type, got class path.to.TestSomething.
Are you missing the use of TypeToken idiom?
See http://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Gener
at com.google.gson.TypeInfoFactory.getActualType(TypeInfoFactory.java:97)
...
So, what do I have to do to get this work?
I don't know the answer, but I know that generic type resolution is a tricky thing to get right: specifically full type resolution from interface with type parameter T up through to generic parameter declaration (T=Long). In these cases it is not enough to check for Method object's parameters but also resolve generic type parameters. This is most likely what causes issues; it may be a bug in Gson.
Since you are serializing things, perhaps you could just omit any type declarations? Although your TypeToken is correct for the use case, maybe it confuses Gson.
But just in case you could not make Gson work with this, I know that of other JSON libraries Jackson can handle such cases correctly.
Perhaps this issue was resolved in one of the Gson releases newer than what the original questioner was using, because the example in the original question now serializes as expected.
// output:
// [{"test":1},{"test":2},{"test":3}]
import java.util.Arrays;
import java.util.Collection;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
public class Foo
{
public static void main(String[] args)
{
Collection<TestSomething> tests = Arrays.asList(
new TestSomething(1),
new TestSomething(2),
new TestSomething(3));
String json = new Gson().toJson(tests, new TypeToken<Collection<TestSomething>>() {}.getType());
System.out.println(json);
}
}
class TestSomething extends Identifiable<Long>
{
Integer test;
public TestSomething(Integer test)
{
this.test = test;
}
#Override
Long getId()
{
return new Long(test);
}
#Override
void setId(Long t)
{
this.test = (int)(t.longValue());
}
}
abstract class Identifiable<T>
{
abstract T getId();
abstract void setId(T t);
}
Related
I'm attempting to use Spring's org.springframework.core.ResolvableType to figure out the parameterized type at runtime as such:
public class MyClass<T> implements MyInterface<T> {
private final Class<T> typeClass;
public CustomStateSerializer() {
ResolvableType type = ResolvableType.forClass(getClass());
ResolvableType genericType = type.getGeneric();
this.typeClass= (Class<T>) genericType.resolve();
}
}
...
new MyClass<MyType>();
Unfortunately, genericType results to ?. Clearly I'm not using it correctly and I can't seem to find any good docs for the solution.
The class is only has reference to which is not defined yet. So you can pass the instance that will have an specific already:
ResolvableType type = ResolvableType.forClass(getClass(), this);
this.typeClass = type.getGeneric(0).resolve();
I want to know if reference to an instance method of an arbitrary object of a particular type is serializable or not?
Example:
public class MyClass {
public void foo() {
System.out.println("Serializable");
}
}
SerializableConsumer
#FunctionalInterface
public interface SerializableConsumer<T> extends Consumer<T>, Serializable {
}
and field is:
SerializableConsumer<MyClass> serializableMethod = MyClass::foo;
EDITED
Assuming that SerializableFunction refers to a type that extends Serializable, the method reference will be serializable. There is nothing special about the particular type of method reference your are asking for.
Most notably, the “reference to an instance method of an arbitrary object” is not capturing any instance of MyClass, hence, the fact that MyClass isn’t Serializable is not important. It would be different if you were referring to an instance method of a particular instance like object::foo, as in that case, the object had to be serialized as well, which will fail at runtime, if its class doesn’t implement Serializable.
What will not work, is to refer to a void method as a Function of return type Void. I don’t know how your SerializableFunction<MyClass, Void> is defined, but if it is equivalent to Function<MyClass, Void>&Serializable, it will not work.
When you have an appropriate functional interface, serializing the method reference is no problem:
import java.io.*;
import java.util.function.Consumer;
public class MyClass {
public void foo() {
System.out.println("Serializable");
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
Consumer<MyClass> consumer = (Consumer<MyClass>&Serializable)MyClass::foo;
byte[] serialized;
try(ByteArrayOutputStream baos=new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(consumer);
oos.flush();
serialized=baos.toByteArray();
}
Consumer<MyClass> deserialized;
try(ByteArrayInputStream bais=new ByteArrayInputStream(serialized);
ObjectInputStream ois=new ObjectInputStream(bais)) {
deserialized = (Consumer<MyClass>)ois.readObject();
}
deserialized.accept(new MyClass());
}
}
As said, references to a specific instance have to serialize the target instance, hence, depend on the serializability of that instance so
import java.io.*;
import java.util.function.Consumer;
public class MyClass {
public void foo() {
System.out.println("Serializable");
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
Runnable runnable = (Runnable&Serializable)new MyClass()::foo;
byte[] serialized;
try(ByteArrayOutputStream baos=new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(runnable);
oos.flush();
serialized=baos.toByteArray();
}
Runnable deserialized;
try(ByteArrayInputStream bais=new ByteArrayInputStream(serialized);
ObjectInputStream ois=new ObjectInputStream(bais)) {
deserialized = (Runnable)ois.readObject();
}
deserialized.run();
}
}
will fail at runtime with a java.io.NotSerializableException: MyClass, unless you change MyClass to implement Serializable.
I know you can serialize a lambda expresion (as you can see here)
Now, what you want to do is only serialize a the variable by itself? or the method?... I don't know why, but I don't think you can. Maybe you can go for other way, like creates a lambda and serialize it, like in the post above:
Runnable r = (Runnable & Serializable)() -> System.out.println("Serializable!");
I have some fields in a model that I only want to be returned when the logged in user has the role ROLE_ADMIN. I can use #JsonIgnore but that hides it for everyone. How can I make it hide dynamically?
You should use Jackson Json Views technology to acheive it - it allows to choose a different set of fields to be serialized programatically. It is also supported by Spring
Consider you have a class Model with two properties: commonField which should be available for everyone and secretField which should be available only for certain users. You should create an hierarchy of views (any classes would work) and specify which field is available in which view using #JsonView annotation
package com.stackoverflow.jsonview;
import com.fasterxml.jackson.annotation.JsonView;
public class Model {
public static class Public {}
public static class Secret extends Public {}
#JsonView(Public.class)
private String commonField;
#JsonView(Secret.class)
private String secretField;
public Model() {
}
public Model(String commonField, String secretField) {
this.commonField = commonField;
this.secretField = secretField;
}
public String getCommonField() {
return commonField;
}
public void setCommonField(String commonField) {
this.commonField = commonField;
}
public String getSecretField() {
return secretField;
}
public void setSecretField(String secretField) {
this.secretField = secretField;
}
}
Now you can specify the view you want to use in concrete ObjectMapper
package com.stackoverflow.jsonview;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
import static org.junit.Assert.*;
/**
*/
public class ModelTest {
#Test
public void testSecretField() throws JsonProcessingException {
Model model = new Model("commonField","secretField");
assertEquals("{\"commonField\":\"commonField\",\"secretField\":\"secretField\"}", new ObjectMapper().writerWithView(Model.Secret.class).writeValueAsString(model));
assertEquals("{\"commonField\":\"commonField\"}", new ObjectMapper().writerWithView(Model.Public.class).writeValueAsString(model));
}
}
I am not sure if you can use declaratie approach to make spring choose the right view based on user role out of the box, so probably you will have to write some code like this:
#RequestMapping("/data")
public String getData(HttpServletRequest request) {
Model model = service.getModel();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper = request.isUserInRole("ROLE_ADMIN") ? objectMapper.writerWithView(Model.Secret.class) : objectMapper.writerWithView(Model.Public.class);
return objectMapper.writeValueAsString(model);
}
I solved this after literally a full month of trying various things. I'm working with Spring 4.3.1 and boot, with data being returned in Hal using a pagedrepository.
extend RepositoryRestMvcConfiguration as MyRepositoryRestMvcConfiguration and add #Configuration to the class, make sure your starter class has #EnableWebMvc
add this to MyRepositoryRestMvcConfiguration- extend TypeConstrainedMappingJackson2HttpMessageConverter as MyResourceSupportHttpMessageConverter
add this to MyRepositoryRestMvcConfiguration
#Override
#Bean
public TypeConstrainedMappingJackson2HttpMessageConverter halJacksonHttpMessageConverter() {
ArrayList<MediaType> mediaTypes = new ArrayList<MediaType>();
mediaTypes.add(MediaTypes.HAL_JSON);
if (config().useHalAsDefaultJsonMediaType()) {
mediaTypes.add(MediaType.APPLICATION_JSON);
}
int order = config().useHalAsDefaultJsonMediaType() ? Ordered.LOWEST_PRECEDENCE - 10
: Ordered.LOWEST_PRECEDENCE - 1;
TypeConstrainedMappingJackson2HttpMessageConverter converter = new MyResourceSupportHttpMessageConverter(
order);
converter.setObjectMapper(halObjectMapper());
converter.setSupportedMediaTypes(mediaTypes);
converter.getObjectMapper().addMixIn(Object.class, MyFilteringMixin.class);
final FilterProvider myRestrictionFilterProvider = new SimpleFilterProvider()
.addFilter("MyFilteringMixin", new MyPropertyFilter()).setFailOnUnknownId(false);
converter.getObjectMapper().setFilterProvider(myRestrictionFilterProvider);
return converter;
}
Create an empty Mixin
package filters;
import com.fasterxml.jackson.annotation.JsonFilter;
#JsonFilter("MyFilteringMixin")
public class MyFilteringMixin {}
Create an empty Mixin
create class MyPropertyFilter extending SimpleBeanPropertyFilter and override adapt this method
serializeAsField(Object, JsonGenerator, SerializerProvider, PropertyWriter)you need to call either super.serializeAsField(pPojo, pJgen, pProvider, pWriter) or pWriter.serializeAsOmittedField(pPojo, pJgen, pProvider) depending on whether you wish to include or discard this particular field.
I added an annotation to the particular fields I wanted to alter and interrogated that annotation when deciding which of these two to call. I injected the security role and stored permitted roles in the annotation.
This alters what Hal shares out to the caller, not what Hal is holding in its repository. Thus you can morph it depending on who the caller is.
I am fairly new to Neo4J; I am developing a project for learning purposes on which I am facing an issue that I am not managing to solve. My model might be somewhat relational DB influenced, but design issues aside, I believe however that what I am attempting should technically be done.
I have a NodeEntity Foo with an nested object Bar, converted to- and from String via ConversionService. In effect, Bar contains only one single String field, making the mapping trivial.
#NodeEntity
public class Foo {
#GraphId
private Long id;
#Indexed
private Bar bar;
...
}
public class Bar {
private String value;
...
}
When returning from a fairly simple Cypher query defined as follows on my repository:
#RepositoryRestResource(...)
public interface FooRepository
extends PagingAndSortingRepository<Foo, Long> {
...
#Query ("MATCH (foo) RETURN foo.bar")
Iterable<Bar> listBars ();
...
}
Conversion is configured as follows:
#Configuration
#ComponentScan(value = "de.h7r.playground.sd.neo4j",
excludeFilters = #ComponentScan.Filter({ Configuration.class }))
public class PKanbanConfiguration {
#Bean
public ConversionServiceFactoryBean conversionService ()
throws Exception {
final ConversionServiceFactoryBean csfb = new ConversionServiceFactoryBean ();
csfb.setConverters (getConverters ());
return csfb;
}
private Set<Converter> getConverters () {
return Sets.newHashSet (new BarConverter.ToString (), new BarConverter.FromString ());
}
}
Where the code for BarConverter is as follows.
public class BarConverter {
public static class FromString <S extends String, P extends Bar>
implements Converter<S, P> {
#Override
public P convert (final S source) {
// sets value into new instance of Bar and returns
}
}
public static class ToString <P extends Bar, S extends String>
implements Converter<P, S> {
#Override
public S convert (final P source) {
// gets value from Bar and returns
}
}
}
I am getting the following exception.
org.springframework.data.mapping.model.MappingException: Unknown persistent entity test.domain.Bar
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:178)
...
Bar is indeed not a persitent entity nor should in my understanding be one. I grasp that this might have something to do with the defined return type of listBars; on the other hand, the repository if of Foos, so I had expected it to work. I would very much not like to fetch the whole set of nodes and then filter only the nested objects; the same way I would not like to have Bar replaced by String on Foo, since their type might have semantic relevance.
I am a bit lost as to how to returning all the property values for the existing nodes, specially since this query works as expected from neo4j-shell, so I see this as a pure Spring mapping issue.
I can add any further information that might prove helpful upon request.
Any help is very much appreciated.
With this class
#Component
public class Sample {
#Value("${my.name}")
public static String name;
}
If I try Sample.name, it is always 'null'. So I tried this.
public class Sample {
public static String name;
#PostConstruct
public void init(){
name = privateName;
}
#Value("${my.name}")
private String privateName;
public String getPrivateName() {
return privateName;
}
public void setPrivateName(String privateName) {
this.privateName = privateName;
}
}
This code works. Sample.name is set properly. Is this good way or not? If not, is there something more good way? And how to do it?
First of all, public static non-final fields are evil. Spring does not allow injecting to such fields for a reason.
Your workaround is valid, you don't even need getter/setter, private field is enough. On the other hand try this:
#Value("${my.name}")
public void setPrivateName(String privateName) {
Sample.name = privateName;
}
(works with #Autowired/#Resource). But to give you some constructive advice: Create a second class with private field and getter instead of public static field.
Soruce of this info is this: https://www.baeldung.com/spring-inject-static-field
Spring uses dependency injection to populate the specific value when it finds the #Value annotation. However, instead of handing the value to the instance variable, it's handed to the implicit setter instead. This setter then handles the population of our NAME_STATIC value.
#RestController
//or if you want to declare some specific use of the properties file then use
//#Configuration
//#PropertySource({"classpath:application-${youeEnvironment}.properties"})
public class PropertyController {
#Value("${name}")//not necessary
private String name;//not necessary
private static String NAME_STATIC;
#Value("${name}")
public void setNameStatic(String name){
PropertyController.NAME_STATIC = name;
}
}
This is my sample code for load static variable
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component
public class OnelinkConfig {
public static int MODULE_CODE;
public static int DEFAULT_PAGE;
public static int DEFAULT_SIZE;
#Autowired
public void loadOnelinkConfig(#Value("${onelink.config.exception.module.code}") int code,
#Value("${onelink.config.default.page}") int page, #Value("${onelink.config.default.size}") int size) {
MODULE_CODE = code;
DEFAULT_PAGE = page;
DEFAULT_SIZE = size;
}
}
For those who want to use ApplicationContext in the main class of a Spring Boot application, you can just use the return value of SpringApplication.run.
Although workarounds may need to be implemented, one should try to avoid them in most scenarios if possible. Spring is great at handling dependency injection and treats most objects as Singletons. This means that Spring can handle the creation of objects for you, and the injection of these objects at runtime. When combining this with the fact that your Spring managed bean is likely a Singleton, the use of static methods and variables is largely unnecessary. You can simply autowire in an instance of the object you are looking for at the constructor level or variable level and reference the non-static version of the method or variable. This is ideal and behaves similarly to a static reference. Non static variables are basically static because you are only ever using one instance of the object in every part of the code and because of dependency injection you are never handling the instantiation of the object, just like with a static reference! Great! Now I'm sure there are instances where you need the work around (i.e. you aren't using dependency injection or class is not a singleton), but try to not use workarounds if possible. Also this is just my 2 cents. Someone may be able to offer 3. (:
public class InjectableClass{
#Value("${my.value}")
private String myString;
public String nonStaticMethod(){
return myString;
}
}
public class LogicClass{
private InjectableClass injectableClass;
#Autowire
public LogicClass(InjectableClass injectableClass){
this.injectableClass = injectableClass;
}
public void logicClassMethod(){
System.out.println("Hey! Here is the value I set on myString: " +
injectableClass.nonStaticMethod() + ". That was
basically like using a static method!");
}
}