#XmlIDREF not seeing hierarchy when marshalling to XML - java

I'm very new at XML and I'm having a problem I'm not able to solve and after looking around for hours I've decided to post my problem.
I'm using #XmlIDREF to just have the XmlID of some java classes on the XML doc.
All works fine, but when JAXB looks for the ID depeen on the tree hierarchy it seems that it couldn't find it and marhsalls the whole object again.
I'll show you the (simplified) model, it's all about optical routers, java classes represent the router and some components:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class OpticalRouter {
// #XmlID inherited from upper class
private List<FiberConnection> fiberConnections = new ArrayList<FiberConnection>();
}
That's a fiber connection:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class FiberConnection {
#XmlID
private String instanceID;
#XmlIDREF
Card card;
#XmlIDREF
Port port;
#XmlIDREF
Channel channel;
}
And finally:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlSeeAlso({
DropCard.class,
AddCard.class
})
public class Card{
// #XmlID inherited from supper class
}
All works fine when I marshall ports and channels due they don't have subclasses.... but when it's time to marshall the cards if it's an AddCard or a DropCard it marshalls it another time instead of using the IDREF.
It seems that JAXB doesn't find them on the hierarchy...
If I change the original "Card card;" of FiberConnection for an "AddCard" for example it works too (JAXB finds the IdREF and doesn't marshall it again).
Hope I have explained that clearly.
Ask if not, I'll be glad to answer :)
Thanks in advance!
EDITED
Ok, I've come back with new info and results to explain myself better.
Due it's a huge class model and I don't want to make the post to much difficult to read I have created a kind of UML class diagram to make it much easier to read with some important info that should help (XML annotations and parameters). I have also included #XmlElement tags as Blaise advised me (thanks again).
Here you can find the yEd UML archive: yEd file
And there an UML jpg if you don't have/want to download graph
editor: jpg file
I also include (to finish) a part of the XML to better see what's happening.
Here I have a fiberConnection as the above mentioned.
I have a scCard and a fiberChannelPlan that have already been marshalled before on the XML doc (checked) but they are being marshalled again...
In exchange, srcPort, srcChannel and fiberChannels, also marshalled before, have only their ID's.
<OpticalRouter>
<fiberConnections>
<instanceID>FiberConnection#29e83b01</instanceID>
<srcCard xsi:type="DropCard" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<instanceID>DropCard#6b02b23d</instanceID>
<type>11</type>
<subtype>1</subtype>
<chasis>0</chasis>
<fiberChannelPlan xsi:type="ChannelPlan">
<instanceID>ChannelPlan#7e246b6d</instanceID>
<firstChannel>0</firstChannel>
<lastChannel>0</lastChannel>
<maxFreq>196.1</maxFreq>
<minFreq>191.6</minFreq>
<fiberChannels>Channel/360</fiberChannels>
<fiberChannels>Channel/368</fiberChannels>
<fiberChannels>Channel/376</fiberChannels>
<fiberChannels>Channel/384</fiberChannels>
</fiberChannelPlan>
<cardExpressPort>Port#4f781d1d</cardExpressPort>
<carCommonPort>Port#56bf83ad</carCommonPort>
</srcCard>
<srcPort>Port#56bf83ad</srcPort>
<srcChannel>Channel/184</srcChannel>
</fiberConnections>
</OpticalRouter>
I think that the problem is something related to that post I found (even if I'm using linux and java 1.6) or other posts I have seen here on StarckOverflow:
java.net/jira/browse/JAXB-870
Thanks in advance!
P.S: all code I'm using is opensource and can be downloaded from the main source at a git repository if someone thinks it should be easier for him.

You need to make sure that each object in your graph is referenced through a containment/nesting relationship (such as #XmlElement), this forms the XML. I don't see where this requirement is met in your model. Then you can use #XmlID/#XmlIDREF to have key based relationships within the tree to turn it into a graph.
For More Information
http://blog.bdoughan.com/2010/10/jaxb-and-shared-references-xmlid-and.html

Related

Using a SOAP artifact in Java

I've been searching all over and I can't find a simple example for this. I need to call a Web service from my Java application using SOAP. I've run the utility to create all the Java artifacts from the WSDL. Let's say one is called "Customer", and these are the first few lines:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Customer", propOrder = { "id" })
public class Customer {
I assume that I start by creating a new Customer object and setting all the attributes I need. What I need to know is how to take that object and pass it to the service as a SOAP envelope(?). I also have artifacts for submit, like "SubmitCustomer", but again I'm not sure how to take my Customer object and keep going with it.
I'm sure this is a basic question, but all I've been able to find in my searches are examples of creating your own XML, or basic "how to get started with SOAP", or how to generate artifacts, but that's all. If someone can point me to a good resource, that would be great.
Among generated classes must be one extends javax.xml.ws.Service.
Look through this class to find a method annotated with #WebEndpoint.
Open file with definition of return type of this method.
There you will find methods correspond to WSDL operations.

How to make changes in marshalled output in Java web service client

I am trying to interact with a third party web service, who requires me to send a security token as a part of each request. The token is a node by itself, and I acquire it from the response of an initial call.
The web service endpoint is dotNet, and I have a Java client.
Apparently, the server side expects me to send the security token exactly like it was provided to me: literally the same string: so it won't do if its content has a different size, order, etc.
So, in SoapUI, everything works fine. There is a token in the response of the initial 'startSession' call, which I copy into the request of a next call.
But in Java (I tried JAX-WS and CXF generated code, both rely on JAXB) it doesn't work. I receive the token as an object after it is unmarshalled, and I use this object in the next call.
When marshalled and send, it is missing a namespace attribute in a subnode. The server side says it won't continue because the token is incorrect.
So, by using JAXB outbound logical handler functionality, I am able to add the missing namespace without any problems in the DOM source (I was also able to achieve this with a CXF interceptor).
The problem now is, that the attributes, when marshalled, are ordered in such a way that the result still not matches the provided token as it was before it was unmarshalled. Alhough it should not matter, the order of these attributes is crucial.
I have no idea how to solve this, unless it is possible to actually modify the output XML string. I even tried a dirty hack by removing all attributes from the subnode and replacing them with one attribute that visually looks the same; but then the outer two double quotes become single quotes...
I hope anyone has an idea. Because I have none.
Cheers.
UPDATE:
I should have mentioned that the attributes in question are namespace(d) attributes. The node should look like this:
<HawanedoSessionInfo xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.thecompany.com/Hawanedo/Business/v2.0c">
However, after using outbound JAXB handler to add the missing xmlns="...", my result looks like this:
<HawanedoSessionInfo xmlns="http://schemas.thecompany.com/Hawanedo/Business/v2.0c" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
In the HawanedoSessionInfo class, I used XmlType.proporder and #XmlAttribute like so:
#XmlType(name = "HawanedoSessionInfo", propOrder = {
"xsd",
"xsi",
"xmlns",
and some other non-attribute sub-elements..
private String xsd;
private String xsi;
private String xmlns;
#XmlAttribute(ns="http://schemas.thecompany.com/Hawanedo/Business/v2.0c")
public String getXsd() {
return xsd;
}
public void setXsd(final String xsd) {
this.xsd = xsd;
}
#XmlAttribute(ns="http://schemas.thecompany.com/Hawanedo/Business/v2.0c")
public String getXsi() {
return xsi;
}
public void setXsi(final String xsi) {
this.xsi = xsi;
}
#XmlAttribute
public String getXmlns() {
return xmlns;
}
public void setXmlns(final String xmlns) {
this.xmlns = xmlns;
}
So apparently the proporder option does not help in this case?
UPDATE 2:
Like I wrote in my answer, it now works. Based on this LINK,
in the HawanedoSessionInfo class I added:
#XmlCustomizer(HawanedoSessionInfoCustomizer.class)
I created the customizer class exactly as described in the linked page, and I added the jaxb.properties.
So I did two things:
1) I added my attributes to (the top of the already existing) propOrder attribute. I added the attributes as instance variables and created the getters/setters. I annotated the getters with XmlAttribute.
2) I implemented the XmlCustomizer solution.
Now comes the strange part. According to Fiddler, the order of the attributes is still not changed! But I must stress that this is now working, ONLY after implementing the Customizer. What is happening here? :)
So in principle you cannot control order of attributes in a standard way, but ....
Depending on jaxb /java version the order can be determined by alphabetical order of the names, the order of declaration.
You could try in your code if a) moving the fields around changes anything, b) renaming the fields (the XMLAttribute than have to map to original name).
If you are lucky, it will work. But of course it is a hack and will work till next jaxb/java update.
The JAXB providers (the actuall implementation can have extra features), that can be used to customized the marshalling process). For example I found that: https://community.oracle.com/thread/977397 abut eclipselink.
I am sure there was a way of intercepting the soap body before it is send or governing the data serialization before it is send. I can think how it was called but try to google the jaxws client customization. If you capture the whole soap message simple xslt transforamation could fix the attributes order.
I feel your pain. The whole point of using xml, jaxws and such is to make our life easier and then someone providers decide not to follow standards and you end up with a mess that you were trying to clean for few days. Good luck and maybe try to contact xml gurus from Eclipse Moxy
I am so happy right now, because I got it working and it only cost me a full week to do so...:) With help of #Zielu, I was pointed to this link with the EclipseLink XMLCustomizer solution as suggested by Blaise Doughan: XMLCustomizer solution
I took the code in my original question (underneath 'UPDATE') and added the exact solution as suggested. Not sure if it is all necessary, but it works. Thanks guys.
It's possible you can control the order by using,
#XmlType (propOrder={"prop1","prop2",..."propN"})

Marshalling nested objects with JAXB - unwrapped

I've got a very simple problem here, but after using Google for over an hour now, I still cannot find a good solution and it starts to cost too much money..
In my app I use REST as an API, basically only with JSON as payload type, and Enunciate for documentation of the API. As you might know, enunciate will generate an xsd schema from the classes. I am therefore in the situation, that I need to configure all the DTO's for Jackson/JSON handling with the suitable annotations, and also with JAXB annotation to ensure the generated schema is correct and can be parsed with XJC into the correct classes!
Although that is not very difficult to achieve and works flawless in most of the cases, I have a simple but somehow special case, in which it completely fails.
Assuming the following classes:
#JsonRootName(value = "location")
public class Location {
private String label;
#JsonUnwrapped
private Address address;
// constructors, getters, setters ommited..
}
//// new file
public class Address{
private String city;
private String street;
private String postCode;
}
This works 100% with Jackson/JSON. The embedded Address object will be unwrapped so that the JSON looks like this:
{
"label":"blah",
"street":"Exchange Road",
"city":"Stacktown"
"postCode":"1337"
}
This works forth and back with Jackson.
JAXB on the other hand is able to parse (most of) the Jackson annotations, so that generally you wont have problems with using simple objects in both worlds. #JsonUnwrapped though sadly is NOT supported by JAXB, and strangely that (from my POV) quite simple usecase seems to be not reflectable with any JAXB annotation at all.
What happens is, that the generated schema contains the embedded Address object, without any attributes/elements. Therefore the class generated by XJC will contain that Address object reference. And this ultimately leads to erroneous JSON from a app that will use the schema to generate objects...
Any ideas?
The JAXB (JSR-222) specification does not define an equivalent to Jackson's #JsonUnwrapped. For a long time we have offered that functionality in the EclipseLink MOXy implementation of JAXB via our #XmlPath extension.
#XmlPath(".")
private Address address;
For More Information
I have written more about MOXy's #XmlPath extension on my blog:
http://blog.bdoughan.com/2010/07/xpath-based-mapping.html

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?

JAX-WS and Enunciate - How to change Enunciate's default XSD naming convention

I'm using Enunciate to generate a SOAP endpoint for a Wicket web application I am working on and I have a couple of questions that I haven't figured out the solution to yet.
1 How do I change the name of the xsd files? I've looked through the FAQ and it tells me to do something similar to this:
<xml>
<schema namespace="http://api.example.com/data" file="data.xsd"/>
</xml>
However, I haven't quite figured out how to set the targetNamespace for my data objects. I've done this for my service via #WebService ( targetNamespace="blah" ), but how do I annotate my data objects to let Enunciate know which namespace they should belong to?
2 Enunciate generates my XSDs just fine, but I don't particularily like the element names it uses. I have a ServiceRequest and ServiceResponse object. The ServiceRequest object has a List of User objects. The ServiceResponse has a list of Group objects. Enunciate suggests that every "User" object within the ServiceRequest should be using the tag "<users>". I feel that it would make more sense to use the singular form, "<user>" since the tag does in fact only contain a single user. Is it possible to change this behaviour, and if so, how?
Thanks in advance.
So just to be clear, with the exception of the question about naming your schema files, your questions are really more about JAXB than they are about Enunciate. JAXB is the spec that defines how your Java objects are (de)serialized to/from XML and Enunciate conforms to that spec.
Anyway, the easiest way to apply a namespace to your Java objects is with a package-info.java file in the package of your Java classes. Annotate your package with #XmlSchema and set the namespace to be the value you want.
Customizing how your accessors are serialized to/from XML can be done with the #XmlElement annotation, e.g.:
public class MyClass {
...
#XmlElement (name="user")
List<User> users;
...
}
Here are the JAXB javadocs
https://jaxb.dev.java.net/nonav/2.1.9/docs/api/
Or google for a good JAXB tutorial.

Categories