Estou tentando me integrar a um sistema de terceiros e, dependendo do tipo de objeto, o elemento raiz do documento XML retornado é alterado. Estou usando a biblioteca JAXB para Marshalling / unmarshalling.
Raiz1:
<?xml version="1.0" encoding="UTF-8"?>
<root1 id='1'>
<MOBILE>9831138683</MOBILE>
<A>1</A>
<B>2</B>
</root1>
Root2:
<?xml version="1.0" encoding="UTF-8"?>
<root2 id='3'>
<MOBILE>9831138683</MOBILE>
<specific-attr1>1</specific-attr1>
<specific-attr2>2</specific-attr2>
</root2>
Estou consumindo todos os XMLs diferentes, mapeando-os para um objeto genérico :
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "ROW")
public class Row {
@XmlAttribute
private int id;
@XmlElement(name = "MOBILE")
private int mobileNo;
@XmlMixed
@XmlAnyElement
@XmlJavaTypeAdapter(MyMapAdapter.class)
private Map<String, String> otherElements;
}
E o adaptador para transformar os valores conhecidos em um mapa :
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.parsers.DocumentBuilderFactory;
import java.util.HashMap;
import java.util.Map;
public class MyMapAdapter extends XmlAdapter<Element, Map<String, String>> {
private Map<String, String> hashMap = new HashMap<>();
@Override
public Element marshal(Map<String, String> map) throws Exception {
// expensive, but keeps the example simpler
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Element root = document.createElement("dynamic-elements");
for(Map.Entry<String, String> entry : map.entrySet()) {
Element element = document.createElement(entry.getKey());
element.setTextContent(entry.getValue());
root.appendChild(element);
}
return root;
}
@Override
public Map<String, String> unmarshal(Element element) {
String tagName = element.getTagName();
String elementValue = element.getChildNodes().item(0).getNodeValue();
hashMap.put(tagName, elementValue);
return hashMap;
}
}
Isso colocará o ID e o número do celular nos campos , e o restante, o desconhecido em um mapa .
Isso funciona se o elemento raiz estiver fixo em ROW, como no exemplo acima.
Como fazer isso funcionar de modo que o elemento raiz seja diferente em cada XML? Uma maneira de talvez sermos agnósticos para enraizar o elemento enquanto não organizamos?
Respostas:
Esqueça o JAXB para isso e analise você mesmo com o StAX.
No código abaixo, alterei o campo
mobileNo
deint
paraString
, pois o valor9831138683
é muito grande para umint
.Teste
Resultado
fonte
xml
desarquivar vs via JAXB (para objetos de dados conhecidos onde o XSD é definido), o que seria melhor. Mesmo se eu usar o StAX, preciso serializar novamente oRow
objeto para o xml adequado com o elemento raiz correspondente.Row
classe se lembre do nome. --- Se você precisar cuidar da entrada JSON, use um analisador JSON.mobileNo
uma String. Mesmo seint
fosse grande o suficiente para armazenar um número de telefone, o valor não representa um valor. Na IMO, isso garante que seja tratado como uma sequência, mesmo que o valor seja numérico.Eu não acho que exista uma maneira de fazer o que você está pedindo. No XML, o nó raiz (o documento) deve ter um elemento (ou classe) definido. Em outras palavras,
xs:any
funciona apenas para subelementos. Mesmo se houvesse uma maneira de conseguir isso, IMHO esta é uma má decisão. Em vez de criar um elemento raiz variável ("dinâmico"), você deve adicionar um atributo name ao mesmo elemento para distinguir os arquivos XML. Por exemplo:Para isso, basta adicionar um atributo de nome ao seu elemento existente:
fonte
XmlRootElement
a partirRow
e unmarshalling aJAXBElement
por:JAXBElement<Row> element = unmarshaller.unmarshal(new StreamSource(xml), Row.class); Row root = element.getValue();
. Isso permite preencher todos os campos e consumir XMLs com diferentes elementos raiz.xs:any
. Eu tentei isso com o esquema XML também. Se você definir seu elemento raiz como basicamente qualquer coisa, seu esquema será interrompido. Em outras palavras,xs:any
só pode ser usado no contexto de um nó filho. Por fim, você precisa ir ao seu cliente e mostrar por que essa é uma implementação ruim.XmlRootElement
deRow
funcionará ao desarmar. Minha pergunta para você é: como você o organizaria se fosse necessário? Você pode fazer isso com outros mecanismos, mas não com o JAXB. A desvantagem dessa abordagem é que você precisará escrever seus próprios validadores. Possível, mas você perde os recursos nativos do JAXB para fazer isso.