I am getting a list of objects from 3rd party but it will always contain one object only. So at my end in target I have created it as an object rather than list. That object contains multiple lists inside it just like source object.
This is how I am trying to map a list to an object. ChargeTransaction contain orderInvoice as an object and not a list. For list which are inside ChargeTransaction I have created separate mappers. I dont want to write java code in #afterMapping because then how nested lists will be mapped. The nested lists are of type in both the objects.
#Mapping(target = "orderInvoice", source = "basePaymentRequest.invoice.eventPayload.orderInvoices")
ChargeTransaction createInvoiceCTMapper(PaymentTriggerBaseModel basePaymentRequest, ChargeType chargeType);
Error
java: Can't map property "List<OrderInvoice> basePaymentRequest.invoice.eventPayload.orderInvoices" to "OrderInvoice orderInvoice". Consider to declare/implement a mapping method: "OrderInvoice map(List<OrderInvoice> value)".
I tried
#Mapping(target = "orderInvoice", expression= "java(basePaymentRequest.invoice.eventPayload.orderInvoices.get(0))")
But it gives error in Impl class
chargeTransaction.setOrderInvoice( basePaymentRequest.invoice.eventPayload.orderInvoices.get(0) );
java: incompatible types: com.sams.oms.ng.common.models.payment.request.OrderInvoice cannot be converted to com.sams.oms.ng.common.models.payment.cosmos.OrderInvoice
IMHO the best way to solve this problem is to use a #Named paired with #Mapping#qualifiedByName
#Mapper
class Mapper {
#Mapping(target = "orderInvoice", source ="basePaymentRequest.invoice.eventPayload.orderInvoices", qualifiedByName="firstElement")
ChargeTransaction createInvoiceCTMapper(PaymentTriggerBaseModel basePaymentRequest, ChargeType chargeType);
#Named("firstElement")
OrderInvoice map(List<OrderInvoice> value) {
if(value == null) return null;
if(value.isEmpty()) return null;
return map(value.get(0));
}
abstract com.sams.oms.ng.common.models.payment.request.OrderInvoice map(com.sams.oms.ng.common.models.payment.cosmos.OrderInvoice invoice);
}
In this way you are instructed MapStruct to use map(List<>) to convert invoices to a single OrderInvoice and abstract map(OrderInvoice) to let MapStruct autogenerate mapping code.
Code in untested because I haven't limited spare time today,but I hope my example may be useful;if anything is wrong feel free to comment and I will correct code asap.
Related
Im having some issues trying to convert a List of Object to a custom DTO. However, all the answers that i found are focused on converting Entities or POJ0s to DTO. How can i do this in either some kind of iteration, or even manyally accesing all the Object properties?
Right now i have something like this that throw some casting errors, but idk if changing datatypes would work or if i should try something else.
List<Object> ls = myDAO.getSomethingFromDB();
List<MyDTO> ls2 = new ArrayList<MyDTO>();
for(Object o : ls){
ls2.add((MyDTO) o);
}
Also, first StackOverflow questing, sorry if im asking something dumb or in a bad way.
You can't directly convert Object to MyDTO until unless myDAO.getSomethingFromDB(); is returning list of MyDTO
Learn more about ClassCastException here:
https://docs.oracle.com/javase/8/docs/api/java/lang/ClassCastException.html
Explanation of ClassCastException in Java
if you really want to convert to MyDTO then you need to create new MyDTO object then set the values to this object.
There are probably a lot of ways to do this, here is one of those ways using Java Streams:
List<MyObject> objects = // Get objects from database
List<MyDto> dtos = objects.stream().map(myObj -> {
MyDto newDto = new MyDto();
// Set your properties here, in this example i'm setting a name and description:
newDto.setName(myObj.getName());
newDto.setDescription(myObj.getDescription());
// Repeat for every property
return newDto;
})
.collect(Collectors.toList());
I am using the map function to create a new DTO with the same properties as my object in the list.
I have thumbnails saved in my database as a byte array. I can't seem to workout how to return these to the frontend clients via GraphQL.
In a standard REST approach I just send a POJO back with the bytes and I can easily render that out.
However trying to return a byte[] is throwing
Unable to match type definition (ListType{type=NonNullType{type=TypeName{name='Byte'}}}) with java type (class java.lang.Byte): Java class is not a List or generic type information was lost: class java.lang.Byte
The error is descriptive and tells me what's wrong, but I don't know how to solve that.
My thumbnail.graphqls looks like:
type Thumbnail {
id: ID!
resource: [Byte!]
}
And the thumbnail POJO
public class Thumbnail extends BaseEntity {
byte[] resource;
}
I'm using graphql-spring-boot-starter on the Java side to handle things, and I think it supports Byte out the box, so where have I gone wrong?
Very fresh to GraphQL so this could just be an obvious mistake.
Cheers,
You have to serialize it to one of the standard types.
If you want your byte array to look like a string such as "F3269AB2", or like an array of integers such as [1,2,3,4,5] its totally up to you.
You can achieve the serialization by writing a resolver for your entity, like that:
public class ThumbnailResolver extends GraphQLResolver<Thumbnail> {
public String resource(Thumbnail th) { ... }
//or List<Integer> resource(Thumbnail th) { ... }
//or whatever
}
The resolver have always priority over your entity. This means that if a resolver method with the correct name, parameters and return type is found in the resolver class, this will be called instead of the entity method. This way we can "override" entity methods, in order to return an other result, even a different type than the actual entity field. By using resolvers, we could also have access to application scoped services etc that an entity typically does not have.
After writing your resolver, don't forget to update your schema file to:
resource: String
#or resource:[Int]
#or whatever
Your schema should refere to the resolver type since this is what graphQL recieves. The actual entity type will become then irrelevant to graphQL.
As a plan B, you could implement a new Scalar. This would be like inventing a new basic type. This is also not that hard. You can see the already existing scalar types here and do something similar.
You can then name your new type ByteArray or something like that, declare it in your schema:
scalar ByteArray
and then use it.
I would go for the first solution though since it is easier and faster to implement.
I have a generic getter trait
trait Getter[A] {
def get: A
}
and I would like to parse JSON into a List of objects implementing this trait. Two such implementations:
case class CoalesceGetter[A](getters: List[Getter[String]]) extends Getter[A] {
override def get: A = getters.map(_.get).find(_ != null).orNull
}
case class AsnGetter(ipGetter: Getter[String]) extends Getter[Long] {
override def get: Long = 99L // dummy function
}
I would like to parse JSON into the correct Getter class based upon a property called function which corresponds to the class and type which corresponds to the generic type in the case of getters which need a generic (both properties are strings in the json blob I'm parsing). I've looked at custom serializers for json4s but don't see how to work with generics. Any help is appreciated!
First of all, I don't think it is a good idea to jsonify classes with type argument. I think it is a better design to define non-typed (case) classes that are direct equivalent of your json object, and use standard read/write json as provided by many libraries.
But then, to answer your question, I'd like to return another question: how would you do it "manually"?
I.e. how would you write and read different CoalesceGetter[A] with different A?
Here is a proposition: put the type arg in a json field:
"ofInt": {"type-arg":"Int", "getters":[ ... list of getters in json ...]},
"ofDouble":{"type-arg":"Double", "getters":[ ... list of getters in json ...]}
Now, if you'd write the reader, how would you instantiate the 2 ofInt and ofDouble, knowing the type-arg "Int" and "Double" (which are string!).
I see 2 solutions:
1) Either you have a hard-coded map of arg-type string => actual scala type
argType match{
case "Int" => new CoalesceGetter[Int](...)
case "Double" => new CoalesceGetter[Double](...)
}
2) Or you store and read a generalized type as string value in the arg-type string, such as the java Class.forName (see [https://stackoverflow.com/a/7495850/1206998] for example). But this is a really really bad idea IMHO.
(note: if you want to serialize any object just to reload it later or on another computer, don't use json but dedicated serialization such as the Java Serialization or kryo that is used by spark)
What I am trying to do is to map a List of entities to a list of their String ids (more or less) using Dozer.
Obviously, it implies Custom Converter. My first idea was to make a converter from MyEntity to a String, and then say to Dozer something like "Map every object of this collection using this converter". But I couldn't figure out how to do so.
So my second idea was to make a converter form a list of entities to a list of string, directly. My problem on this idea is that I was strugling on something ridiculous which is to get the type of my list in the constructor, as below (which doesn't work at all):
public MyEntityListConverter() {
super(List<MyEntity>.class, List<String>.class);
}
I don't know how to pass an instantiated list's class in a single row wihout declaring anything.
So if someone know either :
How to specify to dozer an object convertor to use in collection mapping
How to get instantiated list type
A third/better solution to try
The way you tried is not possible due to generic types. And if it was, Dozer cannot detect types at runtime.
1st solution with List<>
Your converter :
public class MyEntityToStringConverter extends DozerConverter<MyEntity, String> {
// TODO constructor + impl
}
Your mapping :
mapping(MyEntityA.class, MyEntityB.class)
.fields("myEntityList", "myStringList",
hintA(MyEntity.class),
hintB(String.class));
mapping(MyEntity.class, String.class)
.fields(this_(), this_(), customConverter(MyEntityToStringConverter.class));
2nd solution with list wrappers
You can try to create your custom classes extending a list impl.
public class MyEntityList extends ArrayList<MyEntity> {
}
public class MyStringList extends ArrayList<String> {
}
Change your field in the parent classes you want to map.
Your converter :
public class MyEntityToStringConverter extends DozerConverter<MyEntityList, MyStringList> {
// TODO constructor + impl
}
Your mapping :
mapping(MyEntityA.class, MyEntityB.class)
.fields("myEntityList", "myStringList", customConverter(MyEntityToStringConverter.class));
Another option would be
super((Class<List<MyEntity>>) (Class<?>) List.class,(Class<List<String>>) (Class<?>) List.class);
I very much inclined to #Ludovic solution, but there might be a catch as mentioned in my comment up there.
But a slight tweak works for me though - register the custom converter in "configuration" rather than field level. I'm using XML config but it should work with coding config:
<configuration>
<custom-converters>
<converter type="f.q.c.n.MyEntityToStringConverter">
<class-a>java.lang.String</class-a>
<class-b>f.q.c.n.MyEntity</class-b>
</converter>
</custom-converters>
</configuration>
Converting JSON to Java
The above question is with reference to what has been described on the above thread. There are so many API(s) which provide the flexibility to return responses either in XML or JSON. **I would like to know if there is a way to automatically construct the java bean corresponding to a JSON response. **
lets say you get an object like
[
{
"name":"Java 6 Greatest Hits",
"Author":"Jim Bob Jones",
"price":10.25
},
{
"name":"How to raise a goat",
"Author":"Sir Paxton",
"price":55.97
},
{
"name":"Snow - It is cold",
"Author":"Dr. White",
"price":9.99
}
]
And you want a class like
public class Book{
private String author;
private String name;
private Number price
}
with getters and setters
One option is to use a service like JSONGen, which will create that class. You need to use it first, and include the generated code in your project.
Another option could be dynamically generate the class using javassist or CGLib, but that class would be useless unless you use reflection to access its members, so even if it would be a class, it will behave like a really annoying Map. In no way will be better that simple using JSONObject
seems a simple Message Type Entity not meet you requirement ?
if you want convert a json to an existed and known java bean class,
many lib can do so, like
http://json-lib.sourceforge.net/apidocs/net/sf/json/class-use/JSONObject.html
JSONObject.toBean(JSONObject jsonObject, Class beanClass)
Creates a bean from a JSONObject, with a specific target class.
btw, if you are communicating with restful webservice, org.springframework.web.client.RestTemplate will help you get direct bean result
insteadof json.
if class does not exists, you need program with java reflect mechanism.
try use CGLIB ,http://cglib.sourceforge.net/, dynamic create some class like BeanMap. i wrote a simple sample,
but be ware, opearting class byte is hard and you may meet strange trouble with JVM . Strongly not encourage to do so.
public static BeanMap generateBean(JSONObject json) {
BeanGenerator generator = new BeanGenerator();
Iterator keys = json.keys();
while (keys.hasNext()) {
Object key = keys.next();
Object value = json.get(key);
Class keyClass = guessValueClass(value);
generator.addProperty(key.toString(), keyClass);
}
Object result = generator.create();
BeanMap bean = BeanMap.create(result);
keys = json.keys();
while (keys.hasNext()) {
Object key = keys.next();
Object value = json.get(key);
bean.put(key, value);
}
return bean;
}
/**
* TODO fix guess
*/
static Class guessValueClass(Object value) {
try {
Integer.parseInt(value.toString());
return Integer.class;
} catch (NumberFormatException e1) {
}
try {
Double.parseDouble(value.toString());
return Double.class;
} catch (NumberFormatException e1) {
}
return String.class;
}
I believe the main issue here is that the JSON response lacks type information and last time I checked :-) in Java you need to declare the type of a class property. So some heuristics will be needed to infer the type form the value in the JSON response.
For a related question here in SO have a look at: Generate Java class from JSON?
Yes check out http://flexjson.sourceforge.net
If you're wanting to generate Java classes from JSON, perhaps you could try Jackson. It provides a lot of JSON-related functionality, including the ability to generate bytecode from arbitrary JSON. See this blog post for details.
If you're using Jackson (the most popular library there), try
https://bitbucket.org/astav/jsontojava/wiki/Home
Its open source and anyone should be able to contribute.
Summary
A JsonToJava source class file generator that deduces the schema based on supplied sample json data and generates the necessary java data structures.
It encourages teams to think in Json first, before writing actual code.
Features
Can generate classes for an arbitrarily complex hierarchy (recursively)
Can read your existing Java classes and if it can deserialize into those structures, will do so
Will prompt for user input when ambiguous cases exist