martedì 24 settembre 2013

Java esempio completo creazione xml con JAX-B

In questa classe vediamo un esempio completo di generazione completa dell'xml.
In particolare abbiamo:
  • generazione di attributi a livello di nodo padre e di nodi figli;
  • liste WRAPPED oppure no sia di semplici valori che di oggetti (se si aggiunge l'annotation @XmlElementWrapper si racchiude una collection di valori dentro un nodo "padre")
L'unico punto che mi rimane da capire meglio riguarda proprio la generazione degli attributi su un singolo nodo di un xml.
Bisogna per fare ciò creare una classe che rappresenta il nostro nodo con attributi, e gli attributi saranno proprietà di questa classe, solo che verranno annotati con @XMLAttribute.
Il problema è che se definisco questi attributi come campi pubblici nessun problema, se invece decido di esporli come proprietà con getter e setter ho il seguente errore nella creazione dell'xml :

com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions

dove le 2 illegal annotation sono proprio quelle messe nei set delle property del bean usato per mappare l'attributo.

Di seguito riporto l'xml che si vuole ottenere:


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persona nota="test">
    <nome>carlo</nome>
    <cogn>resi</cogn>
    <info cf="dsdfsdfsd" age="35"/>
    <lista>abc</lista>
    <lista>def</lista>
    <lista>ghi</lista>
    <contenitore>
        <elemento>abc</elemento>
        <elemento>def</elemento>
        <elemento>ghi</elemento>
    </contenitore>
    <elemento>
        <codice>001</codice>
        <descrizione>A01</descrizione>
    </elemento>
    <elemento>
        <codice>002</codice>
        <descrizione>A02</descrizione>
    </elemento>
    <elemento>
        <codice>003</codice>
        <descrizione>A03</descrizione>
    </elemento>
    <contenitoreCodici>
        <elemento>
            <codice>001</codice>
            <descrizione>A01</descrizione>
        </elemento>
        <elemento>
            <codice>002</codice>
            <descrizione>A02</descrizione>
        </elemento>
        <elemento>
            <codice>003</codice>
            <descrizione>A03</descrizione>
        </elemento>
    </contenitoreCodici>
</persona>


Classe Persona.java


package it.prova;
import java.util.Collection;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
@XmlRootElement(name="persona")
@XmlType(name="persona",propOrder={"nome","cognome","info","campiVari","campiVari2","listaNotWrapped","listaWrapped"})
public class Persona {
 private String nome;
 private String cognome;
 private String nota;
 private Info info;
 private Collection campiVari;
 private Collection campiVari2;
 private Collection listaNotWrapped;
 private Collection listaWrapped;
 @XmlElement(name="elemento")
 public Collection getListaNotWrapped() {
  return listaNotWrapped;
 }
 public void setListaNotWrapped(Collection listaNotWrapped) {
  this.listaNotWrapped = listaNotWrapped;
 }
 
 @XmlElementWrapper(name="contenitoreCodici")
 @XmlElement(name="elemento")
 public Collection getListaWrapped() {
  return listaWrapped;
 }
 public void setListaWrapped(Collection listaWrapped) {
  this.listaWrapped = listaWrapped;
 }
 @XmlElementWrapper(name="contenitore")
 @XmlElement(name="elemento")
 public Collection getCampiVari2() {
  return campiVari2;
 }
 public void setCampiVari2(Collection campiVari2) {
  this.campiVari2 = campiVari2;
 }
 @XmlElement(name="lista")
 public Collection getCampiVari() {
  return campiVari;
 }
 public void setCampiVari(Collection campiVari) {
  this.campiVari = campiVari;
 }
 @XmlElement(name="info")
 public Info getInfo() {
  return info;
 }
 public void setInfo(Info info) {
  this.info = info;
 }
 @XmlAttribute(name="nota")
 public String getNota() {
  return nota;
 }
 public void setNota(String nota) {
  this.nota = nota;
 }
 @XmlElement(name="nome")
 public String getNome() {
  return nome;
 }
 public void setNome(String nome) {
  this.nome = nome;
 }
 @XmlElement(name="cogn")
 public String getCognome() {
  return cognome;
 }
 public void setCognome(String cognome) {
  this.cognome = cognome;
 }
}


Classe Info.java (per gli attributi)

package it.prova;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement
public class Info {
 @XmlAttribute(name="age")
 public String eta;
 @XmlAttribute(name="cf")
 public String cf;
// cosi' non funziona!! 
// private String eta;
// private String cf;
// @XmlAttribute(name="age")
// public String getEta() {
//  return eta;
// }
// public void setEta(String eta) {
//  this.eta = eta;
// }
// @XmlAttribute(name="cf")
// public String getCf() {
//  return cf;
// }
// public void setCf(String cf) {
//  this.cf = cf;
// }
// 
}


Classe Campi.java ( qui abbiamo i campi della lista composta da oggetti )


package it.prova;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Campi {
 private String codice;
 private String descrizione;
 public Campi(){
  
 }
 public Campi(String codice,String descrizione){
  this.codice=codice;
  this.descrizione=descrizione;
 }
 public String getCodice() {
  return codice;
 }
 public void setCodice(String codice) {
  this.codice = codice;
 }
 public String getDescrizione() {
  return descrizione;
 }
 public void setDescrizione(String descrizione) {
  this.descrizione = descrizione;
 }
}



Classe GenerateXml.java per la generazione dell'xml


package it.prova;
import java.io.File;
import java.io.FileWriter;
import java.util.*;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class GenerateXml {
 public static void main(String[] args) throws Exception{
  File f=new File("out/test.xml");
  Persona p=new Persona();
  p.setCognome("resi");
  p.setNome("carlo");
  p.setNota("test");
  Info i=new Info();
  i.cf="dsdfsdfsd";
  i.eta="35";
//  i.setCf("sdfsdsdf");
//  i.setEta("33");
  p.setInfo(i);
  List l=new ArrayList();
  l.add("abc");
  l.add("def");
  l.add("ghi");
  p.setCampiVari(l);
  p.setCampiVari2(l);
  List listaCampi=new ArrayList();
  listaCampi.add(new Campi("001", "A01"));
  listaCampi.add(new Campi("002", "A02"));
  listaCampi.add(new Campi("003", "A03"));
  p.setListaWrapped(listaCampi);
  p.setListaNotWrapped(listaCampi);
  JAXBContext jc=JAXBContext.newInstance(Persona.class);
  Marshaller m=jc.createMarshaller();
   m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
         FileWriter fw=new FileWriter(f);
         m.marshal(p, fw);
         fw.flush();
 }
}

Java validare xml con xsd usando JAX-B

Per validare un file xml con un xsd occorre procedere in questo modo.
  1. Per prima cosa dall'xsd si genera la classe java corrisponente (tramite il comando xjc.exe );
  2. Quindi i file di input xml in ingresso vengono validati rispetto all'xsd fornito tentando di parsare il file nell'oggetto ottenuto al punto 1
Questo è il codice usato in un esempio (INPUT è un oggetto Java complesso annotato via jax-b ed ottenuto dall'operazione compiuta al punto 1).
Il metodo prende in input il file con l'xsd e l'xml con i dati.

import java.io.*;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
public static boolean valida(File xsd,File fileOrigine) {
try
    {
 SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
 Schema schema = schemaFactory.newSchema(dtd); 
 JAXBContext jc=JAXBContext.newInstance(INPUT.class);
 Unmarshaller um = jc.createUnmarshaller();
 um.setSchema(schema);
 INPUT i=(INPUT) um.unmarshal(fileOrigine);
        return true;
    }
catch(Exception ex){
          ex.printStackTrace();
   return false;
  }
 }


venerdì 20 settembre 2013

Export runnable jar in modalità package oppure extract

Nel caso in cui da Eclipse si voglia esportare in un unico jar sia il codice della nostra applicazione sia quello delle librerie utilizzate da essa, si può procedere in 3 modi:
  • Extract required libraries into generated jar;
  • Package required libraries into generated jar;
  • Copy required libraries into a sub-folder next to the generated JAR.
Il terzo modo è intuitivo, tutti i jar sono inseriti in una cartella a fianco del jar principale.

Nel primo caso (EXTRACT) il contenuto dei jar viene esploso dentro il jar (Eclipse correttamente avvisa che trattandosi di un repackaging di jar esterni potrebbero esserci problemi di licenza).
In questo caso nel manifest del jar abbiamo la seguente situazione:

Main-Class: classe con il nostro main

Nel secondo caso (package) i jar sono inseriti all'interno del jar esportato, e il manifest risulta modificato in questo modo:

Manifest-Version: 1.0
Rsrc-Class-Path: ./ logback-core-1.0.9.jar commons-lang-2.6.jar logbac
 k-classic-1.0.9.jar slf4j-api-1.7.2.jar log4j-1.2.17.jar commons-code
 c-1.6.jar....[lista di tutti i jar utilizzati]
Class-Path: .
Rsrc-Main-Class:classe con il nostro main
Main-Class: org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader


Le differenze principali tra i 2 approcci sono:
  1. Usando la modalità package crescono le dimensioni del jar ;
  2. Usando la modalità extract si potrebbe incappare in problemi di licenza sw;
  3. Usando la modalità extract se si aggiorna un jar esterno bisogna rieffettuare il pacchetto, nel caso invece della modalità package basta sostituire il jar interno.