Как задать префиксы для namespace в JAXB?
Предыстория:
Жила была одна система и на выходе выплевывала она xml. Этот xml система получала путем преобразования POJO c помощью JAXB. Вторая система умела генерировать точно такие же POJO, но не умела получать из него xml. Как в первой системе происходило преобразование POJO->xml (кроме того что использовался JAXB) было неизвестно. Моя задача была в том чтобы научить вторую систему получать xml, идентичный первой системе.
Практически с первой попытки мне удалось получить похожий xml. Xml первой и второй системы отличались только namespace-префиксами, а точнее их порядком. Эту проблему и предстояло побороть...
Первый xml (от первой системы):
<ns2:data xmlns:ns2="http://myproject.com/test" xmlns:ns3="http://myproject.com/test/data" > <ns2:result>0</ns2:result> <ns2:list> <ns3:node key="name"> <ns3:val>Ivan</ns3:val> </ns3:node> <ns3:node key="age"> <ns3:val>34</ns3:val> </ns3:node> </ns2:list> </ns2:data>
Второй xml (то что получил я):
<ns3:data xmlns:ns2="http://myproject.com/test/data" xmlns:ns3="http://myproject.com/test" > <ns3:result>0</ns3:result> <ns3:list> <ns2:node key="name"> <ns2:val>Ivan</ns2:val> </ns2:node> <ns2:node key="age"> <ns2:val>34</ns2:val> </ns2:node> </ns3:list> </ns3:data>
Так я делал marshaling:
package com.myproject; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; ... try { JAXBContext jaxbContext = JAXBContext.newInstance(myPojo.getClass()); Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); jaxbMarshaller.marshal(myPojo, System.out); } catch (JAXBException e) { e.printStackTrace(); } } }
Естественно, при сравнении двух xml c разными namespace-префиксами XMLUnit отвечал, что эти xml "не идентичны". Т.к. фактически это 2 одинаковых xml, то я решил просто "правильно" расставить свои префиксы. Конечно, это решение типа "костыль", но как временное оно вполне сгодится. Итак, я просто задал точное соответствие между namespace и префиксами в объекте класса Marshaller. Получилось так:
package com.myproject; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import com.sun.xml.bind.marshaller.NamespacePrefixMapper; ... try { JAXBContext jaxbContext = JAXBContext.newInstance(myPojo.getClass()); Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); NamespacePrefixMapper npm = new NamespacePrefixMapper() { private static final String ns2_uri = "http://myproject.com/exp"; private static final String ns3_uri = "http://myproject.com/exp/data"; @Override public String getPreferredPrefix(String s, String s2, boolean b) { if(ns2_uri.equals(s)) { return "ns2"; } else if(ns3_uri.equals(s)) { return "ns3"; } return s2; } }; mar.setProperty("com.sun.xml.bind.namespacePrefixMapper", npm); jaxbMarshaller.marshal(myPojo, System.out); } catch (JAXBException e) { e.printStackTrace(); } } }
Xml стали идентичными, но при этом мы потеряли гибкость маршалинга, т.е. при изменениях в namespace нам необходимо будет править наш маппинг.
Всем спасибо за внимание!