The following results in a MappingException. Do I need to change my design?
public class Foo extends Bar {
// if class == Foo do not send this over the wire
#JsonProperty(access = Access.WRITE_ONLY)
public List<X> myList;
}
public class Bar {
// if class == Bar send this over the wire
public List<X> myList;
public void methodsThatAccessMyList() {
// multiple methods exists in here accessing myList
// also, other classes exist extending bar,
//so moving these to the children will result in duplicate code
}
}
However, I need the json property on the child class, to prevent the child class to transport that field over the wire.
What do I need to change to prevent the ambiguous mapping?
org.springframework.data.mapping.MappingException: Ambiguous field
mapping detected! Both protected java.util.List ... and
#com.fasterxml.jackson.annotation.JsonProperty(index=-1,
access=WRITE_ONLY, value="", defaultValue="", required=false)protected
java.util.List ... map to the same field name ...! Disambiguate using
#Field annotation!
It turns out you can put the JsonProperty on the getter of the field, it will work as expected.
Like this, you don't need to override the field itself in the extending class.
Related
I have a class Order:
#Data
#Entity
public class Order {
private List<Project> projects;
// more fields
}
I have a two API methods in my controller:
#GetMapping
public ResponseEntity<List<Order>> getOrders() {
return ResponseEntity.ok(orderService.getOrders());
}
#GetMapping("/{id}")
public ResponseEntity<Order> getOrder(#PathVariable long id) {
return ResponseEntity.ok(orderService.getOrder(id));
}
So in this case projects is always sent via JSON, if its present its just getting serialized, if its not present its getting fetched lazily and then serialized. I could avoid it being serialized by annotating the field with #JsonIgnore. But the problem is that i want to send it sometimes and sometimes i dont. For example in getOrders() i dont want the projects to be serialized. In getOrder(...) i would want projects to be serialized. Is there any way to tell during runtime either inside custom code or by an annotation that i want to send it in one specific case and not in another case? The only thing i figured out is that - shortly before serializing - i can initialize projects with null and annotate the entity with #JsonInclude(JsonInclude.Include.NON_NULL). That way it wouldnt be sent and if i want to send it i can just avoid initializing it with null. But obviously i dont want to iterate over each Order in O(n) just to initialize its projects with null.
This is easy to achieve using "JSON Views".
First, define some classes to represent each view (e.g. internal/external):
public class OrderViews {
public static class OnlySomeFields {}
public static class AllFields extends OnlySomeFields {}
}
Next, on your class, assign a view to each field:
public class Order {
#JsonView(OrderViews.OnlySomeFields.class)
private String foo;
#JsonView(OrderViews.AllFields.class)
private String bar;
// getters/setters/etc
}
Then, in your controller, you can specify which view to use for each method:
#RestController
public class MyController {
#JsonView(OrderViews.AllFields.class)
#GetMapping("/with-all-fields")
public Order getOrderAllFields() {
return orderService.getOrder();
}
#JsonView(OrderViews.OnlySomeFields.class)
#GetMapping("/with-some-fields")
public Order getOrderAllFields() {
return orderService.getOrder();
}
}
With this setup, navigating to /with-all-fields returns a JSON containing foo and bar, while navigating to /with-some-fields returns a JSON only containing foo.
You can use this technique to selectively serialize specific fields, and should be able to apply it to your use case.
I have a class
public class SomeClass {
#CustomAnnotation1
String stringProperty = "LALALA";
#CustomAnnotation1
int integerProperty;
#CustomAnnotation1
ClassObject classObject;
}
CustomAnnotation1 is a custom annotation defined by me which can be put over any Field. Suppose class ClassObject is something like
public class ClassObject {
#CustomAnnotation1
public String someOtherString;
public String log;
}
What I want to achieve - If my annotation is put on any field which is not a primitive type, I want to access all the fields of that class.
My Approach - Get all the fields annotated with CustomAnnotation1, iterate over all of them and if it is non-primitive, get all the fields inside that class and process.
What I've tried - I am able to get all the elements annotated with my annotation using the below code in my AbstractProcessor class.
Collection<? extends Element> annotatedElements = roundEnvironment.getElementsAnnotatedWith(CustomAnnotation1.class);
List<VariableElement> variableElements = ElementFilter.fieldsIn(annotatedElements);
Questions -
I've researched a lot about the VariableElement class but unable to find a way to check if the field is primitive or not. Can this be done?
Is there any better approach to achieve this?
VariableElement.asType().getKind().isPrimitive()
Imagine an annotation called "MyAnn" with runtime retention, a class MyClass, and an abstract class called MyData. MyClass has a field of type MyData, annotated with MyAnn. Within the instance of MyData, how do see if the annotation MyAnn is present and retrieve its information?
Note - in Java8 I know we can directly annotate the inner class at construction - and that works - but I need this working based on the field annotation.
Thanks for any help!
public MyClass extends MySuperClass() {
#MyAnn(value = "something")
protected MyData mydata;
public void doSomething() {
mydata = new MyData() {};
mydata.initialize();
}
}
public abstract MyData() {
String myValue = null;
public void initialize() {
if (***I am annotated with MyAnn) {
myValue = (***value from annotation MyAnn);
}
}
}
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnn {
String myvalue;
}
MyData cannot directly know whether it has been annotated with MyAnn, because it has no knowledge of whether the current instance is a field or a standalone instance, and if it is a field, then whether the field has or has not been annotated with MyAnn.
So, you will need to somehow obtain and pass the value to initialize(), and you will need to somehow obtain the value at the place where initialize() is called. And from your code, it appears that "something" can be passed as a parameter to initialize(), making the whole thing a lot easier than annotating the field and then checking whether the field is annotated.
But in any case, if you want to check whether a field is annotated, you have to:
obtain the fields of your class with getClass().getDeclaredFields()
loop over the fields; for each field, either
invoke isAnnotationPresent( MyAnn.class ) or
invoke field.getAnnotations(), loop for each annotation, and check whether this annotation instanceof MyAnn.class
Once you have found the annotation, you can get its value with annotation.value();
I've got a Kotlin class, similar to
data open class MyDto (
var property: String? = null
// ...
)
and a Java class extending this class, similar to
class MySpecificDto extends MyDto {
private String field;
// getter/setter for "field"
public MySpecificDto(final MyDto prototype)
{
super(prototype);
}
}
What is missing in Kotlin's DTO for the "super(prototype)" statement to compile?
MyDto's constructor takes a single parameter of type String, and you are trying to pass it a MyDto.
I think you are looking for --
super(prototype.getProperty());
Data classes seem a like logical base for a hierarchy of DTOs. Unfortunately, they do not play well with inheritance, so doing so is not a good idea. See this answer.
Update to address comment --
For a Kotlin side solution, you need to remember Kotlin classes only allow for a single constructor. For data classes, the format of that constructor is already defined, so you cannot just pass an object and have it work, or define a different constructor. Also, as noted by #bashor in comment to your original question, there is no copy constructor. You can, however, create a separate function to initialize your object if you want --
data open class MyDto (var property: String? = null //...) {
fun init(dto: MyDto) {
property = dto.property
//... rest of the properties
}
}
and the in your Java constructor call init instead of super.
public class MySpecificDto extends MyDto {
private String field;
public MySpecificDto(final MyDto prototype)
{
init(prototype);
}
}
The caveat on this solution is that your data class must provide default values for all of its properties because there is an implicit call to the constructor with zero parameters.
I am trying to annotate my java method as #XmlTransient in my java class like below.
#XmlAccessorType(XmlAccessType.PROPERTY)
public abstract class MyClass {
#XmlTransient
public void addsomething{
// do something
}
}
When I try to use this class in my JaxBContext through other class I am getting following exception
JAXB annotation is placed on a method that is not a JAXB property
this problem is related to the following location:
at #javax.xml.bind.annotation.XmlTransient()
,
But when I see XmlTransient() annotation definition(#Target(value={FIELD,METHOD,TYPE})) it's clearly said be to work with methods. And In the JavaDoc(http://docs.oracle.com/javaee/7/api/javax/xml/bind/annotation/XmlTransient.html) it says
The #XmlTransient annotation can be used with the following program elements:
a JavaBean property
field
class
Can't I use #XmlTransient on methods?
The only methods that #XmlTransient can be used are those that begin with get or set. These methods used in combination are used to expose a property in Java. #XmlTransient can be placed on either the get or set method.
Get Method
The get method must take no parameters and return a value:
public String getFoo() {
return foo;
}
Set Method
The set method must take one parameter.
public void setFoo(String foo) {
this.foo = foo;
}