[XSL-LIST Mailing List Archive Home] [By Thread] [By Date] [Recent Entries] [Reply To This Message] Re: How to declaratively describe a mapping that invol
Hello again Roger and XSL-List, Since I'm still learning XSpec features I thought I would try and improve my answer for you even further - taking a tip from Dimitre N. BTW, for XSpec matters see not only the XSpec wiki but Amanda Galtman's excellent blog at https://medium.com/@xspectacles ... So the test set below improves on what I posted earlier this week: - It calls out some logic using more 'declarative' function variables - It uses re-use logic (x:like/@label) to manage complexity and better express the higher-level rules as abstractions I left out the top-level element here, which includes a binding for the `rg`-prefixed namespace. <!-- Functions returning Boolean --> <x:variable name="rg:is-a-direction" as="function(*)" select="function($i as item()) as xs:boolean { $i = ('North','East','South','West') }"/> <x:variable name="rg:is-numeric" as="function(*)" select="function($i as item()) as xs:boolean { $i castable as xs:double }"/> <x:variable name="rg:has-four-digits" as="function(*)" select="function($i as item()) as xs:boolean { replace($i,'\D','') ! (string-length(.) eq 4) }"/> <!-- using regex instead of checking if $i * 10 is an integer --> <x:variable name="rg:has-one-decimal-precision" as="function(*)" select="function($i as item()) as xs:boolean { matches($i,'\.\d$') }"/> <!-- Functions returning xs:string --> <x:variable name="rg:starting-character" as="function(*)" select="function($i as item()) as xs:string { substring($i,1,1) }"/> <x:variable name="rg:strip-nonnumbers" as="function(*)" select="function($i as item()) as xs:string { replace($i,'\D','') }"/> <!-- General rules for all --> <x:scenario label="Roger's mapping example - prototype" shared="true"> <x:expect label="has its direction marked by expanding its initial letter" test="$x:result/*/magneticVariationEWT/$rg:starting-character(.)" select="$x:context/Magnetic_Variation/$rg:starting-character(.)"/> <x:expect label="has its value marked by expanding the rest of the string" test="$x:result/*/magneticVariationEWT/$rg:strip-nonnumbers(.)" select="$x:context/Magnetic_Variation/$rg:strip-nonnumbers(.)"/> <x:expect label="has its direction marked with a known value" test="/*/magneticVariationEWT => $rg:is-a-direction()"/> <x:expect label="has its value marked as a number" test="/*/magneticVariationValue => $rg:is-numeric()"/> <x:expect label="has a value with the correct length (4 digits)" test="/*/magneticVariationValue => $rg:has-four-digits()"/> <x:expect label="is precise to one decimal place" test="/*/magneticVariationValue => $rg:has-one-decimal-precision()"/> </x:scenario> <!-- Scenarios --> <x:scenario label="Example N0149"> <x:context> <Magnetic_Variation>N0149</Magnetic_Variation> </x:context> <x:like label="Roger's mapping example - prototype"/> <x:expect label="maps into a more explicit form"> <magneticVariation> <magneticVariationEWT>North</magneticVariationEWT> <magneticVariationValue>014.9</magneticVariationValue> </magneticVariation> </x:expect> </x:scenario> <x:scenario label="Example W0150"> <x:context> <Magnetic_Variation>W0150</Magnetic_Variation> </x:context> <x:like label="Roger's mapping example - prototype"/> <x:expect label="does the same"> <magneticVariation> <magneticVariationEWT>West</magneticVariationEWT> <magneticVariationValue>015.0</magneticVariationValue> </magneticVariation> </x:expect> </x:scenario> NB also these tests against a spec are not a substitution for validation of inputs or outputs - what they offer is assurance of the integrity of your process (black box testing), while Garbage In/Garbage Out still applies. p Cheers, Wendell -----Original Message----- From: Piez, Wendell A. (Fed) wendell.piez@xxxxxxxx <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> Sent: Wednesday, March 13, 2024 3:42 PM To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx Subject: Re: How to declaratively describe a mapping that involves breaking a string apart and reassembling the parts with an additional symbol? Hi again Roger, So here is an XSpec I wrote as a starter illustration for you: <x:description xmlns:x="http://www.jenitennison.com/xslt/xspec" stylesheet="roger.xsl" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <x:variable name="cardinal-points" select="'North','East','South','West'"/> <x:scenario label="Roger's mapping example"> <x:scenario label="A magnetic variation"> <x:context> <Magnetic_Variation>N0149</Magnetic_Variation> </x:context> <x:expect label="maps into a more explicit form"> <magneticVariation> <magneticVariationEWT>North</magneticVariationEWT> <magneticVariationValue>014.9</magneticVariationValue> </magneticVariation> </x:expect> <x:expect label="has its direction marked by expanding its initial letter" test="$x:result/*/magneticVariationEWT/substring(.,1,1)" select="$x:context/*/Magnetic-Variation/substring(.,1,1)"/> <x:expect label="has its value marked by expanding the rest of the string" test="$x:result/*/magneticVariationEWT/replace(.,'\D','')" select="$x:context/*/Magnetic-Variation//replace(.,'\D','')"/> <x:expect label="has its direction marked with a known value" test="/*/magneticVariationEWT = $cardinal-points"/> <x:expect label="has its value marked as a number" test="/*/magneticVariationValue castable as xs:double"/> <x:expect label="has a value matching regex '\d\d\d\.\d' - four digits, one decimal place" test="/*/magneticVariationValue => matches('\d\d\d\.\d')"/> </x:scenario> </x:scenario> </x:description> I also have XSLT that passes these tests on inputs like yours. Note that presently certain assumptions are made that are almost certainly wrong. For example I decided to enable any of the four cardinal points 'North', 'East', 'West' and 'South'. (Whereas your description suggests only E and W should be recognized.) And even where the testing is not wrong, it could be extended to be more complete especially around edge cases. It is, however, enough to suggest how a set of tests can fill the 'specification gap' you are noticing between requirements and code. By reflecting the way the results should look, the testing can assure both their quality (however you define that) and their relation to the source data. Naturally, it is possible and even likely that tests will overlap, etc. (as shown here) -- they aim for correctness, legibility and completeness, not efficiency. Importantly, these tests can also be run in an XSpec engine to determine if your XSLT performs the mapping correctly. And of those 'expect' statements, only the first is tied to the input here; the others are more general and could presumably be reused on any case you want to test. Here is my XSLT (for checking the assumptions): <xsl:variable name="directions" as="element()*"> <d>North</d> <d>East</d> <d>South</d> <d>West</d> </xsl:variable> <xsl:template match="Magnetic_Variation" expand-text="true"> <xsl:variable name="initial-letter" select="substring(.,1,1)"/> <xsl:variable name="numeric-value" select="substring-after(.,$initial-letter)[. castable as xs:integer] => number()"/> <magneticVariation> <magneticVariationEWT>{ $directions[starts-with(.,$initial-letter)] }</magneticVariationEWT> <magneticVariationValue>{ format-number($numeric-value div 10, '000.0') }</magneticVariationValue> </magneticVariation> </xsl:template> If it were real, one thing I would consider extending here would be testing to expose the behaviors around badly-formed inputs. Unless I could also conduct testing upstream for example with Schematron on the source data, and maybe even then. As my tai chi teacher says -- enjoy! -- Wendell -----Original Message----- From: Roger L Costello costello@xxxxxxxxx <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> Sent: Tuesday, March 12, 2024 9:21 AM To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx Subject: How to declaratively describe a mapping that involves breaking a string apart and reassembling the parts with an additional symbol? Hi Folks, I am mapping an old XML format to a new XML format. To carry out the mapping, I want to write as little code as possible; instead, I want to declaratively describe the mapping in an XML document and then have a tiny piece of generic code which, with little or no knowledge of the old and new formats, carries out the mappings described in the XML document. I am having a hard time with some descriptions. Here's an illustration: I want to map this old XML: <Magnetic_Variation>W0150</Magnetic_Variation> to this new XML: <magneticVariation> <magneticVariationEWT>West</magneticVariationEWT> <magneticVariationValue>015.0</magneticVariationValue> </magneticVariation> Here is one way to perform the mapping: <magneticVariation> <magneticVariationEWT> <xsl:value-of select=" if (substring($magVar,1,1) eq 'E') then 'East' else if (substring($magVar,1,1) eq 'W') then 'West' else 'True'"/> </magneticVariationEWT> <magneticVariationValue> <xsl:value-of select=" concat(substring($magVar,2,3),'.',substring($magVar,5,1))"/> </magneticVariationValue> </magneticVariation> That is a fine way to perform the mapping. However, it is not the way that I want to do it because the mapping is expressed procedurally, not declaratively. I want the mapping expressed declaratively. I can declaratively express part of the mapping -- map the first character E to East, W to West, T to True -- using this description: <Magnetic_Variation> <mapping> <old> <Magnetic_Variation col="1" length="1">E</Magnetic_Variation> </old> <new> <magneticVariationEWT>East</magneticVariationEWT> </new> </mapping> <mapping> <old> <Magnetic_Variation col="1" length="1">W</Magnetic_Variation> </old> <new> <magneticVariationEWT>West</magneticVariationEWT> </new> </mapping> <mapping> <old> <Magnetic_Variation col="1" length="1">T</Magnetic_Variation> </old> <new> <magneticVariationEWT>True</magneticVariationEWT> </new> </mapping> </Magnetic_Variation> I do not know how to declaratively describe the other part of the mapping -- map dddd to ddd.d (where d = digit). I could do this: <mapping> <old> <Magnetic_Variation col="2" length="4"></Magnetic_Variation> </old> <new> <magneticVariationValue>concat(substring($magVar,2,3),'.',substri ng($magVar,5,1))</magneticVariationValue> </new> </mapping> But that is unacceptable (to me) because the description contains code. How do I declaratively describe this mapping? Bonus points if you can also answer this question: Computer Science Theory Question: If it is impossible to declaratively express the above mapping, does that mean there is a limit to declarative descriptions? Is the set of declarative descriptions smaller than the set of procedural descriptions? /Roger
|
PURCHASE STYLUS STUDIO ONLINE TODAY!Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced! Download The World's Best XML IDE!Accelerate XML development with our award-winning XML IDE - Download a free trial today! Subscribe in XML format
|