XML serialization of nillable elements with attributes in C#

Dit is een statische kopie van het eerdere discussie.kinggemeenten.nl.
Nieuwe discussies kunnen in de GitHub repository 'StUF standaarden' als issue worden opgevoerd.

9 reacties / 0 nieuw
Henri Korver
XML serialization of nillable elements with attributes in C#

Op stackoverflow heb ik een oplossing gepost voor het nillable-probleem in de context van .Net (C#):

http://stackoverflow.com/questions/36936661/xml-serialization-of-nillable-elements-with-attributes-in-c-sharp

Ben benieuwd wat jullie ervan vinden? En of hetzelfde idee gebruikt kan worden om het probleem in Java op te lossen.

Lex Uijthof

Bij het genereren van alleen de entiteiten is er geen probleem in Java.
In Java maak ik gebruik van CXF en bij het genereren van de code worden alle elementen gewrapped in een JAXBElement class.

In een gegenereerd NPS object heb je o.a. de volgende definitie staan m.b.t. element overlijdensdatum.

    @XmlElementRef(name = "overlijdensdatum", namespace = "http://www.egem.nl/StUF/sector/bg/0310", type = JAXBElement.class, required = false)
    protected JAXBElement<DatumMetIndicator> overlijdensdatum;

De getter en setter zijn dan:

    public JAXBElement<DatumMetIndicator> getOverlijdensdatum() {
        return overlijdensdatum;
    }
    public void setOverlijdensdatum(JAXBElement<DatumMetIndicator> value) {
        this.overlijdensdatum = value;
    }

Als ik deze b.v. in een bericht wil plaatsen met een xsi:nil="true", dan gebruik ik het volgende in Java:

    // Maak nieuw DatumMetIndicator object en set NoValue op 'geenWaarde'.
    DatumMetIndicator dmi = new DatumMetIndicator ();
    dmi.setNoValue (NoValue.GEEN_WAARDE);

    // Wrap dit object in een JAXBElement en set de nil="true"
    JAXBElement<DatumMetIndicator> overlijdensdatum = objFact0310.createNPSNININGBasisOverlijdensdatum (dmi);
    datumOverlijden.setNil (true);

    // Plaats overlijdensdatum in het NPS object.
    NPSNININGBasis basis = new NPSAntwoord ();        
    basis.setOverlijdensdatum (overlijdensdatum);

    In java is het volgende stukje XSD een probleem bij het genereren:

    <choice minOccurs="0">
        <sequence minOccurs="0">
            <element name="inp.bsn" type="BG:BSN-e" nillable="true" minOccurs="0"/>
            <element name="authentiek" type="BG:Authentiek" default="N" nillable="true" minOccurs="0"/>
        </sequence>
        <element name="anp.identificatie" type="BG:IdentificatieRPSBTL-e" nillable="true" minOccurs="0"/>
    </choice>

Hier staat een choice met een minOccurs="0" waar je kan kiezen uit 2 elementen van verschillende typen.
Bij het genereren van de code om deze choice wordt er een List gemaakt van Objecten omdat deze choice verschillende type (en in java verschillende classes) bevat.
Wanneer ik b.v. inp.bsn nillable wil maken kan dit niet omdat er geen JAXBElement wrapper om het BSNE object kan.
Voor deze elementen moet ik een interceptor gebruiken die in het uitgaande bericht de xsi:nil="true" toevoegt,

Ik ben nog op zoek naar een elegantere oplossing, maar nog niet gevonden.

Henri Korver

Dus als ik het goed begrijp speelt in de Java-wereld een vergelijkbaar probleem als .NET alleen komt het op een andere plek naar boven (in dit geval een choice waarin de subelementen verschillende types hebben). In feite pas jij een vergelijkbare work-around toe als ik heb voorgesteld voor .NET, namelijk het toevoegen van xsi:nil="true" na serialisatie. Alleen hoeft blijkbaar in jouw situatie de xsi:nil="true" niet weer weggehaald te worden voor de deserialisatie.

Met deze  work-arounds voor zowel  Java als .NET is toch prima te leven? M.a.w. is het nog wel echt nodig om de syntax van StUF ingrijpend te veranderen zoals wordt voorgesteld in RFC0413 ...

 

 

Rens Verhage


Met deze  work-arounds voor zowel  Java als .NET is toch prima te leven?

Een gewetensvraag. Ik twijfel. Aan de ene kant is het verleidelijk om hier ja op te zeggen, aan de andere kant heeft die ja een vervelende nasmaak. Wat willen we bereiken? Uiteindelijk willen we bereiken dat verschillende softwaresystemen, geschreven in verschillende talen, op een eenduidige manier met elkaar kunnen communiceren. Nu hebben we in de standaard een constructie bedacht die weliswaar implementeerbaar is, maar waarvoor we wel door de nodige hoepels moeten springen om dat voor elkaar te krijgen. Als leverancier moeten we allemaal workarounds toepassen, om überhaupt een werkend koppelvlak te realiseren / code te genereren.

Wat vinden we belangrijker? Hechten we meer waarde aan een 'zuivere' standaard en nemen we alle implementatie-issues voor lief? Of geven we de voorkeur aan een standaard die meer aansluit op de software die de standaard moet ondersteunen?

Mijn voorkeur gaat uit naar het laatste en dan is RFC0413 zo gek nog niet.  

Henri Korver

Persoonlijk vind ik RFC0413 een paardenmiddel. Fijn dat we dit RFC achter de hand hebben als er echt geen andere oplossing is (was) maar echt vrolijk word ik er niet van. Nog nergens in de wereld heb ik een dergelijk bizarre constructie gezien en ik vrees dat RFC0413 uiteindelijk geen visitekaartje zal zijn voor StUF. Gelukkig is er een hele simpele en eenvoudige oplossing: gebruik een method die xsi:nil="true" toevoegt aan de uitgaande berichten (dus na de serialisatie) en in geval van .Net gebruik ook een method die vóór het deserialiseren het attribute xsi:nil="true" weer weghaalt (in Java is dat niet nodig). Je hoeft deze functies maar één keer toe te voegen aan je code en klaar is Kees. KING zal deze functies beschikbaar stellen, zie de volgende post.

Henri Korver

Hieronder de twee beloofde functies:

        static public XNamespace xsi = "http://www.w3.org/2001/XMLSchema-instance";
        static public XNamespace stuf = "http://www.egem.nl/StUF/StUF0301";

        /// <summary>
        /// - toevoegen xsi:nil="true" in geval van noValue
        /// - verwijderen van elementen met xsi:nil zonder noValue attribute
        /// - leegmaken elementen met noValue attribute
        /// </summary>
        static public XElement addNilAttributes(XElement root)
        {      
            IEnumerable<XElement> noValueElements =
                from el in root.Descendants()
                where (string)el.Attribute(stuf + "noValue") != null
                select el;
           
            foreach (XElement el in noValueElements)
            {
                el.Add(new XAttribute(xsi + "nil", "true"));
                el.ReplaceNodes(null); // make element empty
            }

            IEnumerable<XElement> nilElements =
                from el in root.Descendants()
                where (string)el.Attribute(stuf + "noValue") == null && (string)el.Attribute(xsi + "nil") != null
                select el;
           
            nilElements.Remove();
            return root;
        }

        /// <summary>
        /// Verwijderen xsi:nil="true" attribute uit alle elementen
        /// </summary>
        static public XElement removeNilAttributes(XElement root)
        {
            root.DescendantsAndSelf().Attributes(xsi + "nil").Remove();
            return root;
        }

Henri Korver

@Lex: kun je hier ook de Java-versie van de addNilAttributes functie posten?

Vincent Beukers

Hoi Henri,

 

Ook wij (Gemeente Alkmaar) lopen tegen een .NET probleem aan betreffende de xsi:nil attributen. WCF serialiseert objecten met het kenmerk xsi:nil en negeert daarbij alle attributen (omdat WCF vindt dat het object null is). Zijn jullie al tegen dit probleem aangelopen?

 

Met vriendelijke groet,

 

Vincent Beukers

Gemeente Alkmaar

Wouter van Noort

Dit was het meest eenvoudige stukje van het probleem. Het lastige in .NET is dat als er via de standaard werkwijze een service-client wordt toegevoegd aan een project of een service wordt geimplementeerd op basis van gegenereerde proxy-objecten, deze code ergens in de WCF-pijplijn moet worden geinjecteerd. Het heeft dan ook een behoorlijke impact op performance en memory-gebruik. Vooral bij grote ZKN-berichten kan dit tot problemen leiden.