#XmlPath(".") conflicts with #XmlAdapter - jaxb

having this Jaxb Xml definition, i try to remove the Map Elements Wrapper by adding #XmlPath(".") but it cause exception during the unmarchaling
#XmlRootElement
public abstract class ViewElement{
#XmlJavaTypeAdapter(value=EventAdapter.class)
public Map<Event, String> getEvents() {
}
private transient Class entityType;
public Class getEntityType() {
return entityType;
}
}
And the EventAdapter is
public class EventAdapter extends XmlAdapter<EventAdapter.AdaptedMap, Map<Event, String>> {
public static class AdaptedMap {
#XmlVariableNode("key")
List<AdaptedEntry> entries = new ArrayList<AdaptedEntry>();
}
public static class AdaptedEntry {
#XmlTransient
public String key;
#XmlValue
public String value;
}
.....
}
my output was
<element>
<events>
<onCellEdit>do some thing<onCellEdit>
</events>
<entityType>com.agitech.erp.model.erp.ErpFolder</entityType>
<element>
I try to remove the <events> tag by adding #XmlPath(".")
#XmlPath(".")
#XmlJavaTypeAdapter(value=EventAdapter.class)
public Map<Event, String> getEvents() {
}
The output is good
<element>
<onCellEdit>do some thing<onCellEdit>
<entityType>com.agitech.erp.model.erp.ErpFolder</entityType>
<element>
but the unmarchaling faileds
Caused by: Exception [EclipseLink-3002] (Eclipse Persistence Services - 2.6.0.v20140809-296a69f): org.eclipse.persistence.exceptions.ConversionException
Exception Description: The object [], of class [class java.lang.String], from mapping [org.eclipse.persistence.oxm.mappings.XMLDirectMapping[entityType-->view.entityType/text()]] with descriptor [XMLDescriptor(com.agitech.erp.view.BeanView --> [DatabaseTable(view), DatabaseTable(viewFrame), DatabaseTable(viewElement)])], could not be converted to [class java.lang.Class].
Internal Exception: java.lang.ClassNotFoundException:
at org.eclipse.persistence.exceptions.ConversionException.couldNotBeConvertedToClass(ConversionException.java:95)
at org.eclipse.persistence.internal.helper.ConversionManager.convertObjectToClass(ConversionManager.java:446)
Debuging Jaxb bring me to the line
org.eclipse.persistence.internal.oxm.XMLDirectMappingNodeValue
public void endElement(XPathFragment xPathFragment, UnmarshalRecord unmarshalRecord) {
...
line 205 unmarshalRecord.setAttributeValue(convertedValue, xmlDirectMapping);
}
During the unmarchaling of entityType value, the UnmarshalRecordImpl.currentObj contains the EventAdapter instead of the parent element
I modify org.eclipse.persistence.internal.oxm.record.UnmarshalRecordImpl
public XPathNode getNonAttributeXPathNode(String namespaceURI, String localName, String qName, Attributes attributes) {
....
if(null == resultNode && null == nonPredicateNode) {
// ANY MAPPING
resultNode = xPathNode.getAnyNode();
// by default it return the EventAdapter, changing it to NULL fix my problem
}
....
}
Not a safe solution

I have been able to reproduce the issue that you are seeing, but haven't yet worked out the cause. You can use the following bug to track the progress on this issue:
http://bugs.eclipse.org/457169

After trying a lot of things, I was able to find a workaround for this issue. I thought of posting here the same so it can be helpful to someone else in the future. The lead has confirmed the issue around 5 years ago but seems like they have not fixed it and I was facing a similar issue.
Basically, we can use the beforeMarshal and afterUnmarshal methods to change the values in the fields.
You need to create a field List<Object> with #XmlAnyElement(lax=true) along with Map<String,Object>.
Remove the #XmlPath(".") and the XMLAdapter class.
Mark the field Map<String, Object> with #XmlTransient.
Now within the beforeMarshal and afterMarshal fields, you can exchange the data. During the unmarshal in beforeunmarshal, all the unknown field values will be present within the List<Object> loop over it and add it to the Map<String, Object>.
Similarly during the marshaling, you can move the values Map<String, Object> to List<Object> by creating the DOM elements.
Marshaling all values are added to root as DOM Elements are present and during Unmarshaling known values are read first and then-unknown values are stored within List<Object> due to #XmlAnyElement.
I have created an example using the Customer class, you can modify it accordingly for your need.
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
#JsonInclude(Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
#XmlRootElement(name = "Customer")
#XmlType(name = "Customer", propOrder = {"name", "age", "otherElements"})
#XmlAccessorType(XmlAccessType.FIELD)
#Getter
#Setter
#AllArgsConstructor
#ToString
#NoArgsConstructor
public class Customer {
#XmlTransient
private String isA;
private String name;
private String age;
#XmlAnyElement(lax = true)
#JsonIgnore
private List<Object> otherElements = new ArrayList<>();
#JsonIgnore
#XmlTransient
private Map<String, Object> userExtensions = new HashMap<>();
#JsonAnyGetter
#JsonSerialize(using = CustomExtensionsSerializer.class)
public Map<String, Object> getUserExtensions() {
return userExtensions;
}
#JsonAnySetter
public void setUserExtensions(String key, Object value) {
userExtensions.put(key, value);
}
private void beforeMarshal(Marshaller m) throws ParserConfigurationException {
System.out.println("Before Marshalling User Extension: " + userExtensions);
ExtensionsModifier extensionsModifier = new ExtensionsModifier();
otherElements = extensionsModifier.Marshalling(userExtensions);
System.out.println("Before Marshalling Final Other Elements " + otherElements);
userExtensions = new HashMap<>();
}
private void afterUnmarshal(Unmarshaller m, Object parent) throws ParserConfigurationException {
System.out.println("After Unmarshalling : " + otherElements);
ExtensionsModifier extensionsModifier = new ExtensionsModifier();
userExtensions = extensionsModifier.Unmarshalling(otherElements);
otherElements = new ArrayList();
}
}
You can refer the creation of DOM ELEMENTS here:https://stackoverflow.com/a/24239105/7584240
You can refer my complete answer here: https://stackoverflow.com/a/67923216/7584240

Related

How to persist UserData of Element in JAXB Unmarshal?

I unmarshal the Document to object as below.
Before that, when parsing XML, use setUserData to store location information for each element.
class MyJaxbAdapter extends XmlAdapter<Object, SubObject> {}
#Override
public UnattendComponent unmarshal(Object v) throws Exception {
Node node = (Node) v; // ElementNSImpl; It's probably a newly created object. Because It is different from the document object given by ownerDocument as SAXSource.
node.getUserData(...) // return NULL
}
}
Document document = ...;
unmarshaller.setAdapter(new MyJaxbAdapter());
MyXMLObject object = unmarshaller.unmarshal(new DOMSource(document), MyXMLObject.class).getValue();
But I can't get UserData inside XmlAdapter's unmarshal method. Is there any way to persist UserData?
Locator information is stored in the properties of Element as shown below.
#Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
Element el = document.createElementNS(usedNamespaceUri, qName);
// ...
el.setUserData(
ElementUserData.class.getName(),
ElementUserData.builder()
.lineNumber(locator.getLineNumber())
.columnNumber(locator.getColumnNumber())
.build(),
null);
}
I need the Locator information (UserData) stored by the above code in the unmarshal of the XmlAdapter.
However, there is no userdata in the node passed as an argument to unmarshal .
Sample Code:
https://github.com/joseph-jclab/jaxb-question-01
Not entirely sure if this is something you are looking for but providing it as a reference so you might get some idea to proceed further:
Sample XML:
<root>
<name>Batman</name>
<year>2008</year>
</root>
Root.class:
#XmlRootElement(name = "root")
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
private String name;
private String year;
#XmlJavaTypeAdapter(CustomAdapter.class)
private String after;
private void afterUnmarshal(Unmarshaller m, Object parent) {
after = name;
}
}
CustomAdapter.class:
public class CustomAdapter extends XmlAdapter<String, String> {
#Override
public String unmarshal(String v) throws Exception {
System.out.println("Within Unmarshal : " + v);
return null;
}
#Override
public String marshal(String v) throws Exception {
System.out.println("Within Marshal : " + v);
return null;
}
}
SampleMain.class:
public class SampleMain {
public static void main(String[] args) throws XMLStreamException, JAXBException {
final InputStream inputStream = Unmarshalling.class.getClassLoader().getResourceAsStream("sample.xml");
final XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
final Unmarshaller unmarshaller = JAXBContext.newInstance(Root.class).createUnmarshaller();
unmarshaller.setAdapter(new CustomAdapter());
final Root root = unmarshaller.unmarshal(xmlStreamReader, Root.class).getValue();
System.out.println(root.toString());
Marshaller marshaller = JAXBContext.newInstance(Root.class).createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(root, System.out);
}
}
Output:
Root(name=Batman, year=2008, after=Batman)
Within Marshal : Batman
<root>
<name>Batman</name>
<year>2008</year>
</root>

broadleaf override controller method

I'm new on broadleaf.
I have problem, I wanted to obscure the method that removes my order in admin:
I create controller :
public class NewOrderController extends AdminBasicEntityController {
private static final Logger LOGGER = Logger.getLogger(NewOrderController.class);
protected static final String SECTION_KEY = "order";
#Override
protected String getSectionKey(Map<String, String> pathVars) {
if (super.getSectionKey(pathVars) != null) {
return super.getSectionKey(pathVars);
}
return SECTION_KEY;
}
#Override
#RequestMapping(
value = {"/{id}/delete"},
method = {RequestMethod.POST}
)
public String removeEntity(HttpServletRequest request, HttpServletResponse response, Model model, Map<String, String> pathVars, String id, EntityForm entityForm, BindingResult result, RedirectAttributes ra) throws Exception {
LOGGER.info("wywołanie nadpisane metody: " + NewOrderController.class.toString());
return "String";
}
}
in applicationContext-admin.xml
add :
All the time it calls me the not overwritten method.
When you create a controller, the bean must be in the servlet context, not in the root context. If you are modifying applicationContext-admin.xml then you are actually adding the bean to the root context.
Add your bean to applicationContext-servlet-admin.xml or add a new <component-scan> entry into applicationContext-servlet-admin.xml to scan your new bean.
One more thing: you likely do not want to override the entire AdminBasicEntityController and it looks like you just want to override /order/* methods. In that case, you should annotate your controller with #Controller and add an #RequestMapping for your section key like this:
#Controller
#RequestMapping("/" + SECTION_KEY)
public class NewOrderController extends AdminBasicEntityController {
private static final Logger LOGGER = Logger.getLogger(NewOrderController.class);
protected static final String SECTION_KEY = "order";
#Override
protected String getSectionKey(Map<String, String> pathVars) {
if (super.getSectionKey(pathVars) != null) {
return super.getSectionKey(pathVars);
}
return SECTION_KEY;
}
#Override
#RequestMapping(
value = {"/{id}/delete"},
method = {RequestMethod.POST}
)
public String removeEntity(HttpServletRequest request, HttpServletResponse response, Model model, Map<String, String> pathVars, String id, EntityForm entityForm, BindingResult result, RedirectAttributes ra) throws Exception {
LOGGER.info("wywołanie nadpisane metody: " + NewOrderController.class.toString());
return "String";
}
}

Partial Unmarshalling with JAXB gives IllegalAnnotationexceptions

when partially unmarshalling an XML, I am receiving the following error message:
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 6 counts of IllegalAnnotationExceptions
Property id is present but not specified in #XmlType.propOrder
this problem is related to the following location:
at public java.lang.String xmlFields.Example.getId()
at xmlFields.Example
Property text is present but not specified in #XmlType.propOrder
this problem is related to the following location:
at public java.lang.String xmlFields.Example.getText()
at xmlFields.Example
Property type is present but not specified in #XmlType.propOrder
this problem is related to the following location:
at public java.lang.String xmlFields.Example.getType()
at xmlFields.Example
Property ID appears in #XmlType.propOrder, but no such property exists. Maybe you meant id?
this problem is related to the following location:
at xmlFields.Example
Property Text appears in #XmlType.propOrder, but no such property exists. Maybe you meant text?
this problem is related to the following location:
at xmlFields.Example
Property Type appears in #XmlType.propOrder, but no such property exists. Maybe you meant type?
this problem is related to the following location:
at xmlFields.Example
at com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException$Builder.check(IllegalAnnotationsException.java:91)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:451)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:283)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:126)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(JAXBContextImpl.java:1148)
at com.sun.xml.internal.bind.v2.ContextFactory.createContext(ContextFactory.java:130)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:248)
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:235)
at javax.xml.bind.ContextFinder.find(ContextFinder.java:445)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:637)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:584)
at extractFromXML.ReadFromXML.convertToJavaObject(ReadFromXML.java:72)
at extractFromXML.ReadFromXML.main(ReadFromXML.java:97)
I have the follwing implementation (extracting only a part of the xml seem to work):
#XmlRootElement
#XmlType(propOrder={"ID", "Text", "Type"})
public class Example {
private String id;
private String text;
public Example() {
super();
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
private String type;
}
And this is the part of the xml file:
......<ExampleOptions><Example> <ID>ETZH8</ID> <Text>hello</Text> <Type>VAR</Type> </Example></ExampleOptions>.......
Any ideas?
Thanks
UPDATE #1
I adapted the suggestions by Blaise, but still getting only null values:
Example [id=null, text=null, type=null, getId()=null, getText()=null, getType()=null, getClass()=class xmlFields.Example, hashCode()=18467372, toString()=xmlFields.Example#119ca2c]
UPDATE #2
This is my current code:
#XmlRootElement
#XmlType(propOrder={"id", "text", "type"})
public class Example {
private String id;
private String text;
private String type;
public Example() {
super();
}
#XmlElement(name="ID")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#XmlElement(name="Text")
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
#XmlElement(name="Type")
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
I can't publish the whole XML. It is a nested XML file.
UPDATE #3
The problem is, I only try to extract a part of my xml. Therefore, I have the following main function:
public static void main(String[] args) throws Exception {
try {
XMLInputFactory xif = XMLInputFactory.newFactory();
StreamSource xml = new StreamSource(POI_XML);
XMLStreamReader xsr = xif.createXMLStreamReader(xml);
xsr.nextTag();
while(xsr.hasNext()) {
if(xsr.isStartElement() && xsr.getLocalName().equals("Example")) {
break;
}
xsr.next();
}
JAXBElement<Example> jb;
try {
JAXBContext context = JAXBContext.newInstance(Example.class);
Unmarshaller um = context.createUnmarshaller();
jb = um.unmarshal(xsr, Example.class);
xsr.close();
Example stations = jb.getValue();
System.out.println(stations.toString());
} catch (JAXBException e) {
e.printStackTrace();
}
} catch (FactoryConfigurationError e) {
e.printStackTrace();
} catch (XMLStreamException e) {
e.printStackTrace();
}
}
Moreover, I just tried to extract just an example of one deep path in the nested XML. It looks more than:
<ExampleOptions><Example> <ID>ETZH8</ID> <Text>hello</Text> <Type>VAR</Type> </Example></ExampleOptions><ConnectionCount>1</ConnectionCount><Info>abc</Info>.....
UPDATE #4
I just checked the marshalling example with the following error message:
Exception in thread "main" javax.xml.bind.UnmarshalException
- with linked exception:
[org.xml.sax.SAXParseException; systemId: file:///D:/proj/ConvertXMLToPostgres/input2.xml; lineNumber: 1; columnNumber: 39; Vorzeitiges Dateiende.]
The propOrder is based on the field/property names and not the elements that they are mapped to. You just need to change your propOrder:
#XmlType(propOrder={"id", "text", "type"})
The stack trace also points you in this direction:
Property ID appears in #XmlType.propOrder, but no such property exists. Maybe you meant id?
this problem is related to the following location:
at xmlFields.Example
Property Text appears in #XmlType.propOrder, but no such property exists. Maybe you meant text?
this problem is related to the following location:
at xmlFields.Example
Property Type appears in #XmlType.propOrder, but no such property exists. Maybe you meant type?
this problem is related to the following location:
at xmlFields.Example
For More Information
I have written more about this on my blog:
http://blog.bdoughan.com/2012/02/jaxbs-xmltype-and-proporder.html
UPDATE #1
Thanks. I changed the code, but I am getting null values.
The element names in your XML don't match the default names that JAXB derives from your property names, so you will need to override it with an #XmlElement annotation.
#XmlElement("ID")
public String getId() {
return id;
}
Debugging Tip:
When you run into an issue when unmarshalling, try populating your object model and marshalling it to XML to see what XML is expected.
UPDATE #2
Demo
When I run the following against your updated Example class everything works for me. Does it work for you as well?
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Example.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
StreamSource source = new StreamSource("input.xml");
JAXBElement<Example> result = unmarshaller.unmarshal(source, Example.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(result, System.out);
}
}
Input/Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Example>
<ID>ETZH8</ID>
<Text>hello</Text>
<Type>VAR</Type>
</Example>
UPDATE #3
When I run the Java code from your UPDATE #3 against the model from your UPDATE #2 with the following XML everything works:
<ExampleOptions><Example> <ID>ETZH8</ID> <Text>hello</Text> <Type>VAR</Type> </Example></ExampleOptions>
Can you update your code in UPDATE #3 to include System.out.println(xsr.getNamespaceURI()); to see if there are any namespaces in play?
while(xsr.hasNext()) {
if(xsr.isStartElement() && xsr.getLocalName().equals("Example")) {
System.out.println(xsr.getNamespaceURI());
break;
}
xsr.next();
}

JAXB Marshalling: Creating an empty element with an attribute

I use a JAXB marshaller and I would like to add an empty element with a specific attribute. This is a dummy class:
#XmlRootElement(name="observation")
public class Observation {
#XmlAttribute
public static final String classCode = "OBS";
#XmlAttribute
public static final String moodCode = "EVN";
private String data;
#XmlElement
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
This creates the following XML:
<observation classCode="OBS" moodCode="EVN">
<data>fsdfsdfd</data>
</observation>
Is there any way to add a new element with a specific attribute only (no value at all)? E.g.
<observation classCode="OBS" moodCode="EVN">
<templateId root="2.16.840.1.113883.10.20.1.31"/>
<data>fsdfsdfd</data>
</observation>
This should do it:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "TemplateIdType")
public class TemplateIdType {
#XmlAttribute(name = "root")
protected String root;
// getter and setter
}
(And you add an element of this class to Observation.)

javassist not injecting annotation at existing field

I'm trying to inject JAXB annotation at runtime using Javassist. I have written following code:
public class AssistAnnotationInjector {
public static void addAnnotationRunTime(String className, String fieldName) throws NotFoundException, CannotCompileException, IOException, ClassNotFoundException{
CtClass ctClass = ClassPool.getDefault().get(className);
ClassFile ccFile = ctClass.getClassFile();
ConstPool constPool = ccFile.getConstPool();
AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
Annotation annot = new Annotation("javax.xml.bind.annotation.XmlTransient",constPool);
attr.addAnnotation(annot);
CtField field = ctClass.getDeclaredField(fieldName);
field.getFieldInfo().addAttribute(attr);
System.out.println(field.getAnnotation(XmlTransient.class));
ccFile.setVersionToJava5();
ctClass.writeFile();
}
public static void main (String args[]) throws CannotCompileException, NotFoundException, IOException, SecurityException, NoSuchMethodException, ClassNotFoundException, JAXBException, NoSuchFieldException{
Person<Student> p = new Person<Student>();
p.setName("XYZ");
Student s = new Student();
s.setName("ABC");
s.setId("239423");
p.setPayload(s);
addAnnotationRunTime("RuntimeAnnotation.Person", "name");
Field f = p.getClass().getDeclaredField("name");
System.out.println(f.getAnnotation(XmlTransient.class));
JAXBContext context = JAXBContext.newInstance(p.getClass());
Marshaller mr = context.createMarshaller();
mr.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
mr.marshal(p, System.out);
}
}
And Person.java class is:
#XmlRootElement(name="Person")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlSeeAlso({Student.class})
public class Person <T>{
private T payload;
private String name;
public void setPayload(T payload){
this.payload = payload;
}
public T getPayload(){
return payload;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
}
In AssistAnnotationInjector.java, I am trying to add XmlTransient annotation to 'name' field. But the name field is still coming in marshalling output. Why is it so?
PS: marshal output is :
#javax.xml.bind.annotation.XmlTransient
null
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Person>
<payload xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="student">
<name>ABC</name>
<id>239423</id>
</payload>
**<name>XYZ</name>**
</Person>
name tag was not expected to present in output..
You basicaly have 2 options:
do the modification before you load the class. You can not use reflection in the normal way! One can try to use org.reflections with Maven plugin to pre-fetch classes. See here for more info.
use custom classloader to load the modified class. See here for more info.
After adding the attribute to the field you need to call ctClass.toClass() method,which freezes the class. After this you can check for the annotation.

Resources