I have a JSON map like:
{ "element":"value","element2":"value2",...}
With both key and value strings. I try to read it with an autobean and I get strange exceptions. It should be straightforward, shouldn't?
The error I'm getting is:
[ERROR] [project_name] - The java.util.Map parameterization is not simple, but the getConf method does not provide a delegate
I read the map like that:
final String jsObject = GeneralContextNativeReader.read("globalConf");
GlobalConfFactory globalConfFactory = GWT.create(GlobalConfFactory.class);
Map<String, String> globalConf = AutoBeanCodex.decode(globalConfFactory, Map.class, jsObject).as();
and the factory is defined as:
public interface GlobalConfFactory extends AutoBeanFactory {
AutoBean<Map<String, String>> globalConf();
}
What is wrong with that ?
AFAIK Maps and Lists and other non simple objects can only be reference types and not value types. See here for more details.
Changing the code to this should make it work:
public interface Data {
public Map<String,String>> getGlobalConf();
}
public interface DataFactory extends AutoBeanFactory {
AutoBean<Data> getData();
}
final String jsObject = GeneralContextNativeReader.read("globalConf");
DataFactory dataFactory = GWT.create(DataFactory.class);
Data data = AutoBeanCodex.decode(dataFactory, Data.class, jsObject).as();
Map<String, String> globalConf = data.getGlobalConf();
Your json string has to look something like that:
{"globalConf":{ "element":"value","element2":"value2",...}}
This can also happen if your AutoBean interface contains a static method (new in Java 8).
Related
How to convert List<?> to List in java?
For example I have this class
#Data
public class Example {
private List<?> data;
}
and I used in this function
#PostMapping("/getResult")
#ResponseBody
public Result getResult(#RequestBody String json) {
Gson gson = new Gson();
Example xmpl = gson.fromJson(json, Example.class);
List<MyObject> source = (List<MyObject>)xmpl.getData(); //==> error
// get Result
return result;
}
It will give this error
class com.google.gson.internal.LinkedTreeMap cannot be cast to class com.myproject.MyObject
EDITED:
The real problem is not from converting ? to object, but from converting LinkedTreeMap to the object
WORKAROUND :
String jsonData = gson.toJson(xmpl.getData());
MyObjectBean[] objs = gson.fromJson(jsonData,MyObjectBean[].class);
You could go with two solutions, to start with:
You can change the Generic type, this way You don't say data is any collection, but it's a collection of type <T>. Now You can create classes with given type anywhere You need it.
Generic value <?> means in general that you don't care what is inside, and probably You won't read it anyway. When You are interested only if collection is null or what it's size.
When You need to do something with it, then use Generic types.
Example:
public class Example<T> {
private List<T> data;
}
Now inside of your controller, create a private class, to deserialize your payload.
static class MyObjectExample extends Example<MyObject>{
}
Now you can use it do decode JSON:
MyObjectExample xmpl = gson.fromJson(json, MyObjectExample.class);
List<MyObject> source = xmpl.getData();
Now if your code can be serialized to MyObject it will work.
Spring supports deserialization also.
If you have a #RestController annotation added to your Controller class
Example:
#PostMapping("/getResult")
public Result getResult(#RequestBody MyObjectExample xmpl) {
// get Result
return result;
}
Or you can add
consumes = MediaType.APPLICATION_JSON_VALUE
to your REST method.
Try using Spring to convert your value for you.
You can find more
GetMapping and PostMapping
tutotrial
The real issue is not when converting ? to MyObject, but the LinkedTreeMap to MyObject, from
this explanation
by #harsh
so I did this workaround
String jsonData = gson.toJson(xmpl.getData());
MyObjectBean[] objs = gson.fromJson(jsonData,MyObjectBean[].class);
I have several classes that are doing the same thing : iterates over a List in an object, and add each items in a private field.
I have two objects : MyCustomObject, that have several fields, and ResultOfQuery, where it has a field called data that is a List<Map<String, Object>>.
For example:
private List<MyCustomObject> myCustomObjectList = new LinkedList();
public void setMyCustomObject (ResultOfQuery resultOfQuery){
ObjectMapper objectMapper = new ObjectMapper();
if(resultOfQuery!= null) {
for (Map<String, Object> map : resultOfQuery.getData()) {
myCustomObjectList.add(objectMapper.convertValue(map,
MyCustomObject.class));
}
}
The problem is that I have other classes that does the exact same method, but with another object instead of MyCustomObject.
So I thought that a good idea would be that all of these classes should extends a class that contains this method, and as a parameter it should take first a resultOfQuery, then a list of any objects, and then a Class.
Does it sounds good, or there is a better way to achieve this?
Also, how to give a list of any object ? I tried List<?>, but this shows me the following error :
Error at list add line
You can achieve type safety with generics and inheritance. If you declare a base class having the common stuff like:
public class BaseClass<T> {
private List<T> tList = new LinkedList<>();
private final Class<T> classT;
public BaseClass(Class<T> classT) {
this.classT = classT;
}
public void setObject (ResultOfQuery resultOfQuery){
ObjectMapper objectMapper = new ObjectMapper();
if(resultOfQuery!= null) {
for (Map<String, Object> map : resultOfQuery.getData()) {
tList.add(objectMapper.convertValue(map, classT));
}
}
}
}
Then it is easy to extend it for each different type, like:
public class MyCustomObjectExtendedClass extends BaseClass<MyCustomObject> {
public MyCustomObjectExtendedClass() {
super(MyCustomObject.class);
}
}
I have renamed stuff because it was decoupled from the MyCustomObject.
You can use private List myCustomObjectList = new LinkedList();
Instantiating a class using a raw type (i.e. without a type parameter, as in List list = new ArrayList(3)), is something you shouldn't do, as it is less type-safe, and is only allowed for backwards compatibility.
Link: Java Generics List and ArrayList with and without Parameters
I have a line of code:
private final Map<MyClassA<?>, MyClassB<?>> myMap = new HashMap<>();
Is there any way to define that map in a way that would tell the compiler that the ? in each case must be the same class?
Something like this?
private final <T> Map<MyClassA<T>, MyClassB<T>> myMap = new HashMap<>();
... which is not legal syntax?
It's just a self-learning question at this point.
FWIW, I want to add a method
public <T> MyClassB<T> getForA(MyClassA<T> a) {
return this.myMap.get(a);
}
But I get a compile error unless I can define myMap to insist that both the key and the value wrap the same type.
As you already figured out, you can't do that if key and value are different for different entries:
map.put(new MyClassA<Foo>(), new MyClassB<Foo>());
map.put(new MyClassA<Bar>(), new MyClassB<Bar>());
(I've taken this requirement from your comment)
What you can do is to write some helper methods, which enforce this constraint:
public <T> void put(MyClassA<T> key, MyClass<B> value) {
// Maybe check at runtime if the constraint is not validated?
map.put(key, value);
}
public <T> MyClassB<T> get(MyClassA<T> key) {
// This will produce an unchecked warning.
return (T) map.get(key);
}
As long as you only access the map through such helper methods (and don't use raw types), the constraint on the map will not be violated, which allows you to write type safe code.
The only part that is not typesafe are those helper methods, and that's where you have to be careful.
You can do something similar if you introduce one static inner class for the type you need. For example:
public class DoubleGenericTest<T> {
public static class MapHolder<Z> {
private final Map<MyClassA<Z>, MyClassB<Z>> myMap = new HashMap<>();
}
private final MapHolder<String> stringMap = new MapHolder<>();
private final MapHolder<Integer> integerMap = new MapHolder<>();
}
class MyClassA<X> {}
class MyClassB<Y> {}
This gives you the class you need to hang the type parameter onto. Maybe not ideal in every situation but it's the only thing I can think of.
I have a generic class in java defined as:
public static class KeyCountMap<T>
{
private Map<T, MutableInt> map = new LinkedHashMap<T, MutableInt>();
// ... rest of the properties...
public KeyCountMap()
{ }
#SuppressWarnings({ "unchecked", "rawtypes" })
public KeyCountMap(Class<? extends Map> mapType) throws InstantiationException, IllegalAccessException
{
map = mapType.newInstance();
}
//... rest of the methods...
}
I have defined same class in .NET as:
public static class KeyCountMap<T>
{
private Dictionary<T, MutableInt> map = new Dictionary<T, MutableInt>();
// ... rest of properties...
public KeyCountMap()
{ }
public void KeyCountMap<T>(T obj) where T : Dictionary<T, MutableInt>
{
obj = new T(); // Unable to define new instance of T
map = obj; // Unable to convert T to base class
}
}
And then a method is defined to sort map of type KeyCountMap<T> by value in the descending order . The method is defined as:
public static KeyCountMap<T> SortMapByDescendValue<T>(KeyCountMap<T> _map)
{
List<KeyValuePair<T, MutableInt>> _list = new List<KeyValuePair<T, MutableInt>>(_map.EntrySet());
// whereas _map.EntrySet() return of type HashSet<KeyValuePair<T, MutableInt>>
_list = _list.OrderByDescending(_x => _x.Value).ToList();
KeyCountMap<T> _result = new KeyCountMap<T>();
foreach (KeyValuePair<T, MutableInt> _entry in _list)
{
_result.Put(_entry.Key, _entry.Value);
}
return _result;
}
How can I get corrected the class defined in .NET ?
I assume you know Java erases any generic type information after compiling (there's metadata for variables, but actual objects are void of generic type information). Moreover, your code is not type safe:
#SuppressWarnings({ "unchecked", "rawtypes" })
You're using this because you're creating a non-parameterized instance of Map.
In .NET, you don't get around the type system like this, because generic type information is kept and used at runtime.
Let's see your C# code:
public static class KeyCountMap<T>
A static class in C# is a class that cannot be instanced, it's used for its static members alone. I think you don't want this. Perhaps KeyCountMap is a static nested class in Java, as opposed to an inner class.
In C#, you don't have inner classes. Nested classes don't share data with an instance of the containing class, it's as if the name of the containing class is part of the namespace for the nested class. So, you don't need, and actually don't want, the static keyword here.
{
private Dictionary<T, MutableInt> map = new Dictionary<T, MutableInt>();
In .NET, Dictionary is a class. To keep the intent, you should use IDictionary, the corresponding interface, as the type for the map field.
// ... rest of properties...
public KeyCountMap()
{ }
public void KeyCountMap<T>(T obj) where T : Dictionary<T, MutableInt>
Why the void return type, isn't this a constructor?
In C#, constructors can't be generic. You probably want a Type.
Your C# code just doesn't make sense, so here's what you could do:
public KeyCountMap(Type dictionaryType)
{
if (!typeof(IDictionary<T, MutableInt>).IsAssignableFrom(dictionaryType))
{
throw new ArgumentException("Type must be a IDictionary<T, MutableInt>", nameof(dictionaryType));
}
map = (IDictionary<T, MutableInt>)Activator.CreateInstance(dictionaryType);
}
}
We're checking the type before creating an instance. If we didn't, we would create an instance, the cast would fail and the assignment wouldn't even happen, so the new instance would just be garbage.
It may be that the actual instance will be a proxy; if so, you may not want to check the type before creating an instance.
You can't just copy-paste Java as C# (or vice-versa) and expect to make just a few changes until it works, for some definition of works, e.g. it compiles. The languages are not that similar, and chances are that too many subtle things are wrong.
This approach might be fun at first, but you'll stumble so often it will soon stop being any fun at all. You should learn the basics and understand the way things are done in the target language before you start translating code line-by-line. Many times, you may find that something you had to do in one environment already exists in the other or vice-versa, or that something may take more or less steps to do in the other, etc.
In this particular case, Java made Class be a generic class, while .NET kept Type a non-generic class. In .NET only interfaces and delegates may state generic type covariance or contravariance. This is rather restrictive anyway, if Type was generic, the intended uses could be either covariant or contravariant. But remember that in Java, a generic Class<T> at runtime is as good as Class, it only has any value at compile time and you can tell the compiler you know better anyway, just like you did.
There are two problems. First, you need to tell the compiler that T has a parameterless constructor, so you can call new T(). You can do that by providing the new() argument to the class definition.
You also have to tell the compiler that T is actually the dictionary you are trying to assign, so we have to extend the class a little more:
public class KeyCountMap<K>
{
private Dictionary<K, MutableInt> map = new Dictionary<K, MutableInt>();
// ... rest of properties...
Note that K is the key type of the dictionary, which you didn't specify yet.
Second, the T in your method can be another T than in your class. Omitting that will do the trick:
public void Map()
{
var obj = new Dictionary<K, MutableInt>(); // Unable to define new instance of T
map = obj; // Unable to convert T to base class
}
Maybe this is what you want?
public class KeyCountMap<T>
where T : new()
{
private Dictionary<T, MutableInt> map = new Dictionary<T, MutableInt>();
// ... rest of properties...
public KeyCountMap()
{ }
public KeyCountMap(T obj)
{
obj = new T();
map = (Dictionary<T, MutableInt>)(object)obj;
}
}
I am using an interface method that returns the map with these key, values.
public interface IParse<T> {
Map<String, T> parse(); //T is an enum type
}
Now in the implementation classes, I use the function parse with
public class TestClass1 implements IParse
{
public Map<String, EnumType1> parse()
{
Map<String, EnumType1> map1 = new HashMap<>();
// Logic to fill the map
return map1;
}
}
public class TestClass2 implements IParse
{
public Map<String, EnumType2> parse()
{
Map<String, EnumType2> map2 = new HashMap<>();
// Logic to fill the map
return map2;
}
}
and return the map containing appropriate enum type. But eclipse seems does not like it. One of the help options shows "Infer Generic Type Arguments".
Now in the above case, how do I use properly return the map without any type casting. Is there any other way I can use the interface method with a return value a map containing values as enum class types. Please give me an example on how to do this.
Please let me know if any further details are required.