You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@cxf.apache.org by monitorjbl <mo...@gmail.com> on 2011/02/10 22:52:40 UTC

Removing namespaces

This may be more of a JAXB question, but I'm using CXF to send the serialized
XML and I'm trying to use AbstractPhaseInterceptor<SoapMessage> to fix this
issue so I though I'd ask here. Basically, I'm working with a server I have
no control over and the data it expects to be sent is almost identical to
the response except that it must have a blank namespace. Now, I'm not using
CXF to generate anything except the SOAP service and port classes from the
given WSDLs. The WSDLs have <xsd:any /> tags for their messages, so the data
that gets sent is really up to me and not the schema. I've created several
annotated Java classes and I'm marshalling/unmarshalling them with JAXB, but
because of the tiny, tiny difference between the XML, I can't use the same
annotated Java classes for both replies and requests. I figured it would be
easiest if I just annotated my classes to have no namespaces and find some
way of stripping the server responses of their namespaces; in essence, I
need an XML message sent from the server via SOAP, which looks something
like this:

        <ServerResponse xmlns="http://some.namespace.com/">
               <Entries>
                     <Entry id="test1" firstName="Test1" lastName="User" >
                           <Address />
                     </Entry>
                     <Entry id="test2" firstName="Test2" lastName="User" >
                           <Address />
                     </Entry>
               </Entries>
        </ServerResponse>

transformed into this for unmarshalling:

        <ServerResponse xmlns="">
               <Entries>
                     <Entry id="test1" firstName="Test1" lastName="User" >
                           <Address />
                     </Entry>
                     <Entry id="test2" firstName="Test2" lastName="User" >
                           <Address />
                     </Entry>
               </Entries>
        </ServerResponse>
		
I've been trying to use the AbstractPhaseInterceptor<SoapMessage> method of
getting to the XML content of the message sent FROM the server, but changing
the "xmlns" attribute to be blank seems to have no effect. After my
interceptor blanks the namespace attribute, I still get a JAXB exception
like this:

Unmarshalling Error : unexpected element (uri:"http://some.namespace.com/",
local:"Entries").
Expected elements are <{}Entries>

Here's the handleMessage() method in my interceptor, and as you can see from
the debug code, I know it's being hit:

	public void handleMessage(SoapMessage message) {
		SOAPMessage sm = message.getContent(SOAPMessage.class);
		try {
			if (sm != null) {
				System.out.println("*********************");
				SOAPBody sb = sm.getSOAPBody();
				NodeList nlist = sb.getChildNodes();
				for (int i = 0; i < nlist.getLength(); i++) {
					Node tag = nlist.item(i);
					if (tag.getLocalName() != null) {
						System.out.println(tag.getLocalName());
						NamedNodeMap nmap = tag.getAttributes();
						if(nmap.getNamedItem("xmlns")!=null){
							System.out.println("found namespace attr");
							Node ns = nmap.getNamedItem("xmlns");
							ns.setNodeValue("");
							nmap.setNamedItem(ns);
						}
					}
				}
				message.setContent(SOAPMessage.class, sm);
				System.out.println("*********************");
			}
			
		} catch (SOAPException e) {
			throw new Fault(e);
		}
	}

It isn't shown, but I had code in there to make sure that all of the objects
are live references and that the attribute was actually being changed in the
SOAPMessage object. This all leads me to believe that at the point the
SOAPMessage is available, the namespace is already set somewhere else and I
can't get to it. Is there some way to completely remove the namespace off
the <ServerResponse> element and force that change into all of its children
with CXF interceptors (or some other method)?
-- 
View this message in context: http://cxf.547215.n5.nabble.com/Removing-namespaces-tp3380211p3380211.html
Sent from the cxf-user mailing list archive at Nabble.com.

Re: Removing namespaces

Posted by cogitate <mo...@gmail.com>.
hi sergei:
  thanks for these changes - in 2.2.4 i had custom stream writer to remove
these namespaces by hijacking the output stream.
with the StaxTransformFeature and at the USER_LOGICAL space adding
message.put( "soap.env.ns.map" , nsMap); i am able to completely control
what goes out and into the wire. 

however, i was wondering if you could add support for regex/pattern matching
on strings that would really help a situation like the following xml

out xml going from cxf :
<ns2:WebGet xmlns:ns3="http://schemas.web.com/xml/error/"
xmlns:ns2="http://schemas.web.com/service"><ns2:Buffer><ns2:Name>ROUTE</ns2:Name></ns2:Buffer></ns2:WebGet>

out xml i need :
<WebGet><Buffer><Name>ROUTE<Name><Buffer><WebGet>

of course i can get this result by having an entry in my spring config(as
you illustrated) for both ns2 and ns3 namespaces.

i want to be able to pattern match like : "*schemas.web.com*" or simply drop
all namespace elements. is there a way to do that?




--
View this message in context: http://cxf.547215.n5.nabble.com/Removing-namespaces-tp3380211p4378888.html
Sent from the cxf-user mailing list archive at Nabble.com.

Re: Removing namespaces

Posted by Sergey Beryozkin <sb...@gmail.com>.
Hi

On Fri, Feb 18, 2011 at 2:03 PM, monitorjbl <mo...@gmail.com> wrote:
>
> That's awesome, Sergey. When will this be available?

We're still improving/testing it, I added one fix today, to ensure it
works well with/without  the schema validation being enabled.
particularly in case of the CXF JAXBDataBinding. Realistically we will
be able to 'ship' it with CXF 2.4.0, due in the end of March or so

Please try the snapshots - and if you spot some limitations then it
will help us to improve it in time for 2.4

One thing I do want to enhance is to add the capability to drop
elements using the wildcard. At the moment it's a bit 'shallow' in
that the selected elements will be dropped but their children will
stay and if chidren need to be dropped to then one has to list them
all which is not realistic for complex fragments. I needed that
feature quite badly yesterday while working on the demo :-) but ended
up following a simpler route...

thanks, Sergey

> --
> View this message in context: http://cxf.547215.n5.nabble.com/Removing-namespaces-tp3380211p3391128.html
> Sent from the cxf-user mailing list archive at Nabble.com.
>

Re: Removing namespaces

Posted by monitorjbl <mo...@gmail.com>.
That's awesome, Sergey. When will this be available?
-- 
View this message in context: http://cxf.547215.n5.nabble.com/Removing-namespaces-tp3380211p3391128.html
Sent from the cxf-user mailing list archive at Nabble.com.

Re: Removing namespaces

Posted by Sergey Beryozkin <sb...@gmail.com>.
We've talked on IRC and decided to create a Stax based transform feature.

I'm not sure where to document it yet but see
https://issues.apache.org/jira/browse/CXF-3338 (the SVN Activity tab).

The configuration of this feature will be identical to the way a JAXRS
JAXBElementProvider can be configured:
http://cxf.apache.org/docs/jax-rs-data-bindings.html#JAX-RSDataBindings-CustomizingJAXBXMLandJSONinputandoutput

The merge includes a test showing a SOAP client reading and writing
the unqualified body content (by dropping and adding namespaces as
needed), here is an example:

   @Test
    public void testGetUnqualifiedBookSoap() throws Exception {
        String wsdlAddress =
            "http://localhost:" + PORT +
"/test/services/soap-transform/bookservice?wsdl";
        URL wsdlUrl = new URL(wsdlAddress);
        BookSoapService service =
            new BookSoapService(wsdlUrl,
                                new QName("http://books.com", "BookService"));
        BookStoreJaxrsJaxws store = service.getBookPort();

        // here in/out interceptors are added explicitly, but a
feature can be injected instead
        TransformInInterceptor in =  new TransformInInterceptor();
        Map<String, String> mapIn = new HashMap<String, String>();
        mapIn.put("*", "{http://jaxws.jaxrs.systest.cxf.apache.org/}*");
        in.setInTransformElements(mapIn);

        TransformOutInterceptor out =  new TransformOutInterceptor();
        Map<String, String> mapOut = new HashMap<String, String>();
        mapOut.put("{http://jaxws.jaxrs.systest.cxf.apache.org/}*",
"getBookRequest");
        out.setOutTransformElements(mapOut);


        Client cl = ClientProxy.getClient(store);
        cl.getInInterceptors().add(in);
        cl.getOutInterceptors().add(out);

        Book book = store.getBook(new Long(123));
        assertEquals("id is wrong", book.getId(), 123);
    }

the server:

<jaxws:endpoint xmlns:s="http://books.com"
      serviceName="s:BookService"
      endpointName="s:BookPort"
      id="soapservice-transform"
      implementor="#bookstore-simple"
      address="/soap-transform/bookservice">
      <jaxws:features>
       <ref bean="transformFeatureSoap" />
      </jaxws:features>
   </jaxws:endpoint>

   <bean id="transformFeatureSoap"
class="org.apache.cxf.feature.StaxTransformFeature">
      <property name="outTransformElements">
        <map>
         <entry key="{http://jaxws.jaxrs.systest.cxf.apache.org/}*" value="*"/>
        </map>
      </property>
      <property name="inTransformElements">
        <map>
         <entry key="*" value="{http://jaxws.jaxrs.systest.cxf.apache.org/}*"/>
        </map>
      </property>
   </bean>

This feature works for JAXRS endpoints too

Sergey


On Fri, Feb 11, 2011 at 2:24 PM, monitorjbl <mo...@gmail.com> wrote:
>
> After some more though, I thought of another way that this might be done. If
> there's a way to make the annotated classes somehow inherit the namespace
> from the class that called them, I wouldn't need to do anything else. I
> didn't mention this before, but the server responses are namespaced and
> unqualified, so in just XML terms, this inheritance is already kind of
> happening.  I've tried doing this:
>
> @XmlAccessorType(XmlAccessType.FIELD)
> @XmlRootElement(name = "ServerResponse", namespace =
> "http://some.namespace.com/")
> public class ServerResponse{
>
>        @XmlElement(name = "Entries")
>        protected Entries entries= new Entries();
>
>        public Entries getEntries() {
>                return entries;
>        }
>
>        public void setEntries(Entries entries) {
>                this.entries = entries;
>        }
> }
>
> for the server response and using this for the request:
>
> @XmlAccessorType(XmlAccessType.FIELD)
> @XmlRootElement(name = "ServerResponse", namespace = "")
> public class ServerRequest{
>
>        @XmlElement(name = "Entries")
>        protected Entries entries= new Entries();
>
>        public Entries getEntries() {
>                return entries;
>        }
>
>        public void setEntries(Entries entries) {
>                this.entries = entries;
>        }
> }
>
> In this case the requests work, but the responses don't. JAXB seems to only
> unmarshall the root element, <ServerResponse> unless I explicitly annotate a
> namespace for the child <Entries> element as well. It doesn't throw an
> exception, mind you, it just doesn't set the "entries" member variable to
> anything. If these elements could just inherit the namespace of their
> parents, the way the actual XML seems to be calling for them to do, my
> problem would be fixed by just having two version of the top-level classes.
> --
> View this message in context: http://cxf.547215.n5.nabble.com/Removing-namespaces-tp3380211p3381267.html
> Sent from the cxf-user mailing list archive at Nabble.com.
>

Re: Removing namespaces

Posted by monitorjbl <mo...@gmail.com>.
After some more though, I thought of another way that this might be done. If
there's a way to make the annotated classes somehow inherit the namespace
from the class that called them, I wouldn't need to do anything else. I
didn't mention this before, but the server responses are namespaced and
unqualified, so in just XML terms, this inheritance is already kind of
happening.  I've tried doing this:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "ServerResponse", namespace =
"http://some.namespace.com/")
public class ServerResponse{

	@XmlElement(name = "Entries")
	protected Entries entries= new Entries();

	public Entries getEntries() {
		return entries;
	}

	public void setEntries(Entries entries) {
		this.entries = entries;
	}
}

for the server response and using this for the request:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "ServerResponse", namespace = "")
public class ServerRequest{

	@XmlElement(name = "Entries")
	protected Entries entries= new Entries();

	public Entries getEntries() {
		return entries;
	}

	public void setEntries(Entries entries) {
		this.entries = entries;
	}
}

In this case the requests work, but the responses don't. JAXB seems to only
unmarshall the root element, <ServerResponse> unless I explicitly annotate a
namespace for the child <Entries> element as well. It doesn't throw an
exception, mind you, it just doesn't set the "entries" member variable to
anything. If these elements could just inherit the namespace of their
parents, the way the actual XML seems to be calling for them to do, my
problem would be fixed by just having two version of the top-level classes. 
-- 
View this message in context: http://cxf.547215.n5.nabble.com/Removing-namespaces-tp3380211p3381267.html
Sent from the cxf-user mailing list archive at Nabble.com.