Unmarshalling a date from an XML String to entity using JAXB - java

When I use JAXB, there is something wrong.
I convert entity to a xml String and everything is ok.
But when I convert xml String back to entity, some information is lost (All of them have the same type java.util.Date).
In entity:
public Date flightBaseDate;
In xml:
<flightBaseDate>2013-09-16T00:00:00 08:00</flightBaseDate>
after unmarshalling, getFlightBaseDate() returns null.
I googled.
Following one suggestion, I used # in my entity.
Then it is:
#XmlElement(name = "timestamp", required = true)
public Date flightBaseDate;
I'm sure it will be perfect,
but...throws Exception, like this:
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
Class has two properties of the same name "flightBaseDate"
this problem is related to the following location:
at public java.lang.String com.wonders.nlia.omms.vo.FlightServiceInfoVo.getFlightBaseDate()
at com.wonders.nlia.omms.vo.FlightServiceInfoVo
this problem is related to the following location:
at public java.lang.String com.wonders.nlia.omms.vo.FlightServiceInfoVo.flightBaseDate
at com.wonders.nlia.omms.vo.FlightServiceInfoVo
Why JAXB could not distinguish between the property and its getMethod?
How to solve it?
Platform:jdk7 win7 eclipse tomcat wtp
My Unmarshalling code is:
JAXBContext context = JAXBContext.newInstance(FlightServiceInfoVo.class);
Unmarshaller unMarshaller = context.createUnmarshaller();
FlightServiceInfoVo flightServiceInfoVo =(FlightServiceInfoVo)unMarshaller.unmarshal(new StringReader(flightServiceInfoVoXml));
flightServiceInfoVoXml is a String.

You can configure JAXB in many different ways. You have chosen Annotations to define the binding (this is allright, do not worry).
I strongle recommend you read about that technique first as there are a lot of pitfalls. Here is a link to a good tutorial. Here is the part in the tutorial which explains why your binding does not work: XmlAccessorType part
As for your specific issue:
In general you have to tell JAXB what and how to bind the java object to it's XML representation. If you do not do anything, then by default all public members of your class are bound (as you can read here).
Additionally you have chosen to annotate the getter method of your public member, which then just pushes the same variable twice to your XML which later causes the exception you see.
To fix your error, either specify a different mapping strategy for your class by putting e.g. (#XmlAccessorType(XmlAccessType.NONE)) before your class declaration or move the annotation from the getter method to the property.
By the way: Having a getter method and a public member variable does not make sense at all. So making your member variable private will also fix your issue with JAXB and be a lot better for your class design.

the exception clearly says that the property name is duplicated, so check you class for a property 'flightBaseDae', it should be unique. remove the duplicate then unmarshall it

Related

Using Lombok's SuperBuilder with Hibernate Validator (jakarta.validation.x) annotation on a container type leads to "Type mismatch"

I have a class ActivitiesModel which uses Lombok's SuperBuilder.
import jakarta.validation.NotBlank;
// other imports and statements omitted for brevity.
#Data
#SuperBuilder
#NoArgsConstructor
public class ActivitiesModel {
public static final String ACTIVITIES_NOT_NULL_MESSAGE = "Activities cannot be null";
public static final String ACTIVITY_NOT_BLANK_MESSAGE = "Activity cannot be blank";
#NotNull(message = ACTIVITIES_NOT_NULL_MESSAGE)
private List<#NotBlank(message = ACTIVITY_NOT_BLANK_MESSAGE) String> activities;
}
I am using this builder to create an object of ActivitiesModel, and then validating it using Hibernate's Validator interface:
// Somewhere else in the application.
// Create an object using the builder method.
ActivitiesModel activitiesModel = ActivitiesModel.builder()
.activities(List.of("hello", "world")) // <----- Point A
.build();
// Validate the object using Hibernate's validator.
validator.validate(activitiesModel);
However, running this code gives me the following error:
java.lang.Error:
Unresolved compilation problem:
Type mismatch: cannot convert from List<String> to List<E>
The stack trace seems to be pointing at Point A.
I have tried the following approaches:
Replacing the #SuperBuilder with #Builder and #AllArgsConstructor.
Replacing the message attribute with a string literal instead of a static final variable, i.e:
private List<#NotBlank(message = "Activity cannot be blank") String> activities;
1st approach seems to fix this error, however, it's not something I can use as I need to extend the builder functionality to a subclass of ActivitiesModel. Also, this issue is also present in another abstract class, so the super builder functionality for parent classes is definitely required.
2nd approach also works in solving the error. However, going with it is a bit problematic because I then need to have the same message string in the validation test for this model class, which is something I would like to avoid as it duplicates the string.
Another thing to note is that this error only seems to occur in the presence of an annotation on the generic type parameter of the container, which is NotBlank in this case. It is not influenced by any annotations which are present directly on the field itself (NotNull in this case).
So, all in all, these are the questions that I would like to get some answers to:
Somehow, Lombok is able to figure out the types in case of a string literal but not in case of a static final String. Why is that?
Am I going about this totally wrong? The problem occurs because I'm trying to store the message string in a variable, and I'm trying to re-use the same variable at two places: the annotation's message attribute, and in the validation test for the model class. Should I not be checking for the presence of the message in my validation tests, but be checking for something else instead?
For anyone who comes across this later on, the research for this issue has led me to believe that comparing message strings in tests is not the way to go about writing validation test cases. Another downside to this approach is that you might have different validation messages for different locales. In that case, the message string itself might be a template e.g. my.message.key with its values in a ResourceBundle provided to Hibernate, i.e. files such as ValidationMessages.properties and ValidationMessages_de.properties.
In such a scenario, you could compare message for one locale in your validation test case, however, a better approach might be to check the annotation and the field for which the validation has failed. We can get both of these pieces of information via the ConstraintViolation and subsequently the ConstraintDescriptor types, provided by Hibernate. This way we can circumvent checking the message itself, but rely on the actual validation annotation which has failed.
As for the solution to this question, it seems it was a build cache issue. Cleaning maven's build cache results in this code working perfectly fine, but VSCode still seems to have an issue. For now, I will choose to ignore that.

XStream corrupting values on load and save

My team has recently inherited a codebase that utilizes XStream 1.4.7 to load and save XML for configuration settings which then de-/serializes them from/to custom POCOs. The problem that we're having is that some of the values are getting corrupted during reads or writes. It isn't consistently occurring either which makes it that much more unusual. In most cases, it works perfectly fine with the exact same XML and the exact same POCOs.
A very simplified example (I can't post the exact code and it's quite a bit more complex so I'm going for an easy way to explain what we're seeing) is given the XML:
<monitor>
<autostart>true</autostart>
<name>MYVALUE</name>
</monitor>
Mapped to a POCO:
public class MonitorEntry {
public Boolean autostart;
public String name;
}
Loaded with XStream:
XStream xStream = new XStream(new DomDriver());
xStream.alias("Monitor", MonitorEntry.class);
Monitor monitor = (Monitor)xStream.fromXML(myFile);
The value of name in the Monitor object is read in as arVALUE instead of MYVALUE. The garbage characters at the beginning are what throws things off. Even more strangely, if we change the value of the <autostart> element to false then the XML is mapped correctly and the garbage characters do not appear.
To add to the mystery, on our end we're only seeing the corruption on loading XML to objects, but on one particular customer system they are seeing corruption only when actually saving the XML from objects. In this case, it's exactly the opposite of the above scenario. Given the same POCO with name set to MYVALUE, the actual XML written to the XML file becomes:
<monitor>
<autostart>true</autostart>
<name>arVALUE</name>
</monitor>
Now for a string value such as name it isn't much of a functional issue as it's just a name that is then just spelled wrong but where this becomes a problem is when mapping the XML value to, for example, an enum and the mapping can't find the enum value.
An example being if there is an enum:
public enum Type { VALUE1, VALUE2 };
And the POCO is:
public class MonitorEntry {
public Boolean autostart;
public String name;
public Type type;
}
With the XML:
<monitor>
<autostart>true</autostart>
<name>MYNAME</name>
<type>VALUE2</type>
</monitor>
But the XML value is being read by XStream as erLUE2 then the XStream mapping won't be able to match the correct enum value and throws an exception such as:
No enum const class com.sample.MonitorEntry$Type.erLUE2
We tried updating to XStream 1.4.8 just to see if perhaps something had been fixed but the behavior persists. The codebase is set to compile to Java 1.6 but we've tried 1.6, 7, and 8 as runtimes just to see if it was a runtime bug or something else environmental.
Has anyone else seen similar issues or have any suggestions on what might cause this? I can further update my post to include more detail if necessary. We've used XStream quite a bit before but never had issues.
Edit: We are not currently using any custom converters in this codebase, only the built-in XStream converters.

Java annotations: pass value of annotation attribute to another annotation

I have interface Resource and several classes implementing it, for example Audio, Video... Further, I have created custom annotation MyAnnotation with Class type param:
#MyAnnotation(type = Audio.class)
class Audio {
...
}
#MyAnnotation(type = Video.class)
class Video{
...
}
In some other place in code I have to use Interface Resource as a returned type:
public class Operations<T extends Resource> {
....
#OtherAnnotation(type = Audio.class (if audio), type = Video.class (if video) )
T getResource();
....
}
The question is how to appropriatelly annotate annotation #OtherAnnotation depending of what kind of Resource type will be returned ?
What you are asking is for dynamic values for annotation attributes.
However annotations can only be set at compile time which is the reason why their values can only be compile time constants. You may only read them at runtime.
There was a similar question in which someone tried to generate the annotation value , it's answer explains why there is no way to dynamically generate a value used in annotation in a bit more detail. In that question there was an attempt to use a final class variable generated with a static method.
There are annotation processors which offer a bit more flexibility by handling placeholders. However i don't think this fits your case, as you want the dynamic values at runtime.
This answer refers to spring's use of the expression language for the Value annotation in which the placeholder (#Value("#{systemProperties.dbName})") gets overrided with the data from one of the property sources defined ( example in spring boot )
In any case, you will have to rethink your architecture a bit.

Why "class" cannot be use as tag name in JAXB

I have a POJO which contain a field need to be output to XML with tag name "class".
Using Jersey 2.0, if the client request a JSON response, the JSON object output correctly with attribute name "class".
However, if the client request an XML output, Jersey fail with a HTTP 500 internal error.
Checked that the statement causing the error is
#XmlElement(name = "class")
private int vclass;
Removing the XmlElement annotation and allow the XML to use vclass as tag name work fine.
How could I instruct JAXB to use class as tag name ??
Why “class” cannot be use as tag name in JAXB
You can use "class" as a tag name in JAXB.
What Issue You are Probably Hitting
By default JAXB treats public properties as mapped. Since you annotated a field you were most likely getting an exception about a duplicate mapped property.
Exception in thread "main" com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
Class has two properties of the same name "vclass"
this problem is related to the following location:
at public int forum27241550.Foo.getVclass()
at forum27241550.Foo
this problem is related to the following location:
at private int forum27241550.Foo.vclass
at forum27241550.Foo
Why What You Did Fixed It
You posted the following as an answer:
Finally found out what's wrong.
Don't know why the annotation in variable declaration statement will
cause problem.
Putting the #XmlElement annotation in the setter method work fine.
When you moved the annotation to the property the field was no longer considered mapped, so there was no duplicate mapping problem.
How to Keep the Annotation on the Field
To annotate a field you should use #XmlAccessorType(XmlAccessType.FIELD) on the class.
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Foo {
#XmlElement(name = "class")
private int vclass;
public int getVclass() {
return vclass;
}
public void setVclass(int vclass) {
this.vclass = vclass;
}
}
Finally found out what's wrong.
Don't know why the annotation in variable declaration statement will cause problem.
Putting the #XmlElement annotation in the setter method work fine.

How to ignore/disable/revert/override JAXB class-level #XmlJavaTypeAdapter in certain situations?

Our model classes are annotated with #XmlJavaTypeAdapter (at the class-level). Unmarshalling works fine for the root element and containment/nesting (according to what we implemented in our custom XmlAdapter).
So far, we were happy campers for both XML and JSON serialization/deserialization. However, a new need arose and I can't figure out how to implement it ?
In certain situations, I'd like to be able to "revert" to default JAXB behavior for containment: I want the class-level #XmlJavaTypeAdapter annotation to be ignored/overriden.
I spent hours reading Blaise Doughan's blog (http://blog.bdoughan.com/) and searching StackOverflow and Google but can't find an elegant/pragmatic solution.
Here is a quick setup to illustrate what we currently have (please note that all our JPA/Hibernate/other annotations are not listed for simplicity-sake but they do exist in our model classes (POJOs)):
Class Master
#XmlRootElement
#XmlAccessorType(XmlAccessType.NONE)
#XmlJavaTypeAdapter(XmlMasterAdapter.class)
public class Master {
#XmlElement
private Long masterPrimaryKey;
#XmlElement
private String name;
}
Class Detail
#XmlRootElement
#XmlAccessorType(XmlAccessType.NONE)
#XmlJavaTypeAdapter(XmlDetailAdapter.class)
public class Detail {
#XmlElement
private Long detailPrimaryKey;
#XmlElement
private Master master; // reference/foreign key. No need for #XmlJavaTypeAdapter since it's defined at the class-level in Master.
#XmlElement
private String value;
}
When Master is used as a the root element, the XML is like this:
<master>
<masterPrimaryKey>1234</masterPrimaryKey>
<name>master name</name>
</master>
When Master is used as a contained/nested element, the XML is like this: (thanks to our custom XmlAdapter, the <master> element is "summarized" by its primary key)
<detail>
<detailPrimaryKey>5678</detailPrimaryKey>
<master>1234</master>
<value>detail value</value>
</detail>
So far, everything works fine and we're happy with it.
Now, our new need:
I'd like containment to work in a different way in specific situations.
I want the class-level #XmlJavaTypeAdapter on Master to "temporarily" be ignored/reverted/overridden in a specific context. I'd expect the default JAXB unmarshaller to kick-in (as if there had never been a class-level #XmlJavaTypeAdapter on the contained classes).
Think about a data-import situation where we receive the master and all the details in one payload. As if they were all independent root elements wrapped in a big DTO/transport container.
Here is the XML presenting what we want:
<masterDetailImport>
<master>
<!-- Primary keys omitted because of the import mode -->
<name>master name</name>
</master>
<details>
<detail>
<value>detail 1 value</value>
</detail>
<detail>
<value>detail 2 value</value>
</detail>
<detail>
<value>detail 3 value</value>
</detail>
</details>
</masterDetailImport>
Class MasterDetailImport
#XmlRootElement
#XmlAccessorType(XmlAccessType.NONE)
public class MasterDetailImport implements Serializable
{
#XmlElement
#PLEASE_IGNORE_CLASS_LEVEL_XmlJavaTypeAdapter_AND_UNMARSHAL_AS_IF_IT_WERE_A_ROOT_ELEMENT
private Master master;
#XmlElementWrapper(name="details")
#XmlElement
#PLEASE_IGNORE_CLASS_LEVEL_XmlJavaTypeAdapter_AND_UNMARSHAL_AS_IF_IT_WERE_A_ROOT_ELEMENT
private List<Detail> detail = new ArrayList<Detail>();
}
What I'm looking for is the magic [yet non-existing] #PLEASE_IGNORE_CLASS_LEVEL_XmlJavaTypeAdapter_AND_UNMARSHAL_AS_IF_IT_WERE_A_ROOT_ELEMENT annotation that would allow me to instruct JAXB to do as if #XmlJavaTypeAdapter had never been defined at the class-level for the nested classes.
So far, the solutions we envisioned [and don't like] are:
Create "mirror" DTO objects for deserialization only when we must support import. The are many cons with this approach (duplicate code only used for deserialization, adapters to copy the DTO content into the model class, more unit tests to write/maintain, etc).
Get rid of class-level #XmlJavaTypeAdapter on all our entities we want to be able to import/nest and explicitly use #XmlJavaTypeAdapter on all attributes where nesting/containment is used. I tested this approach and know it would work. However, I think it's error prone and not as elegant as defining it at class-level and be able to have an exception/special-case/override handling telling JAXB to temporarily behave as if it never knew #XmlJavaTypeAdapter has been defined on the class.
I'm running out of ideas here... I tried looking for JAXB's default XML adapter but was not successful: javax.xml.bind.annotation.adapters.XmlAdapter<ValueType,BoundType> is abstract and inherits from Java.lang.Object.
Now, the simple question:
How to implement #PLEASE_IGNORE_CLASS_LEVEL_XmlJavaTypeAdapter_AND_UNMARSHAL_AS_IF_IT_WERE_A_ROOT_ELEMENT ?
Thanks in advance !
An XmlAdapter in JAXB will always be applied, but you can put logic in the XmlAdapter itself to handle your use case. By default a new instance of XmlAdapter will be created each time it will be used, if your XmlAdapter is stateful you can set an instance on the Marshaller or Unmarshaller so that it will be used instead. You can leverage this to help determine if it should be applied or not.
Below is a link to an answer I gave to a related question where a stateful XmlAdapter is used to inline an object the first time it is reference, and then marshal it as a link each subsequent time it is referenced.
Can JAXB marshal by containment at first then marshal by #XmlIDREF for subsequent references?

Categories