2021 Jan 05 3:16 PM
I'm generating the XML for a ZUGFeRD document. It's all working great, except some validators are rejecting my XML due to the namespace prefix.
I've used the ABAP Proxy framework to generate a wsdl from the XSD for ZUGFeRD 2.0 XML, and from that generated an ABAP proxy to get get all the structures and types defined.
I can then use
DATA(with_default_ns) = cl_proxy_xml_transform=>abap_to_xml_xstring(
abap_data = input
ddic_type = 'ZFB_MESSAGE1'
xml_header = 'full' ).
to get the XML. My input is the type that the ABAP Proxy has generated.
The problem is that this creates names spaces like this:
<?xml version="1.0" encoding="utf-8"?>
<n0:CrossIndustryInvoice xmlns:n0="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
xmlns:prx="urn:sap.com:proxy:DEV:/1SAI/TASE493CC7A2F13F4EF8BCD:750"
xmlns:n1="urn:un:unece:uncefact:data:standard:QualifiedDataType:100"
xmlns:n2="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
xmlns:n3="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100">
<n0:ExchangedDocumentContext>
<n2:GuidelineSpecifiedDocumentContextParameter>
<n2:ID>urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_2.0</n2:ID>
</n2:GuidelineSpecifiedDocumentContextParameter>
</n0:ExchangedDocumentContext>
<n0:ExchangedDocument>
<n2:ID>0090000702</n2:ID>
<n2:TypeCode>380</n2:TypeCode>
<n2:IssueDateTime>
<n3:DateTimeString format="102">20190829</n3:DateTimeString>
</n2:IssueDateTime>
<n2:IncludedNote>
But to be completely correct, I need to convert all those nm: prefixes to ZUGFeRD prefixes - specifically
n0: to rsm:, n1: to qdt:, n2: to ram:, n3: to udt:
I've tried to use an XSLT stylesheet to transform the name prefixes. I've tried various form like this:
<xsl:template match="n0:*">
<rsm>
<xsl:apply-templates select="*|node()" />
</rsm>
</xsl:template>
and
<xsl:template match="n0:*">
<xsl:element name="rsm:{local-name()}" namespace="http://sample.com/rsm">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
But just can't get it to work. Judging from the fact that when I set a breakpoint in it at the line after the template match it doesn't stop, it seems it just isn't finding a match.
Anyone have any ideas?
2021 Jan 06 7:55 AM
I can rename the namespace prefixes (source XML with 2 namespace prefixes n0 and n2, result XML with rsm and ram) (NB: your link is very good https://stackoverflow.com/questions/10124887/how-to-replace-namespace-prefix-in-element-and-attribut...
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:sap="http://www.sap.com/sapxsl" xmlns:n0="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100" xmlns:n2=
"urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100" version="1.0">
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates select="*|text()"/>
</xsl:template>
<xsl:template match="n0:*">
<xsl:element name="rsm:{local-name()}" namespace="http://sample.com/rsm">
<xsl:apply-templates select="*|text()"/>
</xsl:element>
</xsl:template>
<xsl:template match="n2:*">
<xsl:element name="ram:{local-name()}" namespace="http://sample.com/ram">
<xsl:apply-templates select="*|text()"/>
</xsl:element>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="."/>
</xsl:template>
</xsl:transform>
I can rename the namespace prefixes (source XML with 2 namespace prefixes n0 and n2, result XML with rsm and ram) (NB: your link is very good https://stackoverflow.com/questions/10124887/how-to-replace-namespace-prefix-in-element-and-attribut...
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:sap="http://www.sap.com/sapxsl" xmlns:n0="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100" xmlns:n2=
"urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100" version="1.0">
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates select="*|text()"/>
</xsl:template>
<xsl:template match="n0:*">
<xsl:element name="rsm:{local-name()}" namespace="http://sample.com/rsm">
<xsl:apply-templates select="*|text()"/>
</xsl:element>
</xsl:template>
<xsl:template match="n2:*">
<xsl:element name="ram:{local-name()}" namespace="http://sample.com/ram">
<xsl:apply-templates select="*|text()"/>
</xsl:element>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="."/>
</xsl:template>
</xsl:transform>
2021 Jan 05 4:23 PM
Could you please name this fake XML validator that is unable to interpret correctly namespace prefixes!!?? I'd say that it's none of your business to spend time because of their buggy validator. The namespace prefixes are not important, what is important is the URI. n0 or rsm (or whatever) pointing to the same URI are completely equivalent.
Concerning the XSLT question:
<xsl:element name="rsm:{local-name()}" namespace="http://sample.com/rsm">2021 Jan 05 4:32 PM
It's not an XML validator, it's a ZUGFeRD validator. E.g. https://www.zugferd-community.net/de/open_community/validation.
Given that ZUGFeRD is a standard use by the German bureaucracy, I wouldn't be surprised if you have to get the namespace right...
Point 2: You mean the XSLT of my transformation. No... I'll give that a go! thanks.
2021 Jan 05 7:29 PM
I agree with sandra.rossi - ZUGFeRD is a certain XML format and general XML rules apply. If the validator rejects generated files, the validator may well be buggy.
you can get the specs here: https://www.ferd-net.de/standards/zugferd-2.1.1/index.html - they contain XSD files that define the various levels of ZUGFeRD definition (minimal, basic, etc) - but you have the XSD, since you generated the proxy from te XSD.
Just use a generic XML validator that validates your generated XML against the XSD files provided by the spec. If your file is fine, raise a ticket with whoever is responsible for the validator that rejects your file.
2021 Jan 06 6:21 AM
I tried various things to get the XSLT to work - but getting nowhere. You'd have thought it was quite a simple thing to do! I was using this as my base https://stackoverflow.com/questions/10124887/how-to-replace-namespace-prefix-in-element-and-attribut...
However, it's all moot, since we're going with "the namespaces don't matter; your validator needs work". I think it's perhaps a shortcut they've taken, as the error message is
"Cannot find the declaration of element 'n0:CrossIndustryInvoice'".
Now using this validator (needs registration, but currently free). https://portal3.gefeg.com/invoice/validation
2021 Jan 06 7:55 AM
I can rename the namespace prefixes (source XML with 2 namespace prefixes n0 and n2, result XML with rsm and ram) (NB: your link is very good https://stackoverflow.com/questions/10124887/how-to-replace-namespace-prefix-in-element-and-attribut...
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:sap="http://www.sap.com/sapxsl" xmlns:n0="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100" xmlns:n2=
"urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100" version="1.0">
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates select="*|text()"/>
</xsl:template>
<xsl:template match="n0:*">
<xsl:element name="rsm:{local-name()}" namespace="http://sample.com/rsm">
<xsl:apply-templates select="*|text()"/>
</xsl:element>
</xsl:template>
<xsl:template match="n2:*">
<xsl:element name="ram:{local-name()}" namespace="http://sample.com/ram">
<xsl:apply-templates select="*|text()"/>
</xsl:element>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="."/>
</xsl:template>
</xsl:transform>
2021 Jan 06 10:12 AM
I had to add the other namespaces, so I've now got:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:n0="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
xmlns:n1="urn:un:unece:uncefact:data:standard:QualifiedDataType:100"
xmlns:n2="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
xmlns:n3="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"
xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
xmlns:qdt="urn:un:unece:uncefact:data:standard:QualifiedDataType:100"
xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"
version="2.0">
<xsl:output encoding="UTF-8" indent="yes" method="xml"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates select="*|text()"/>
</xsl:template>
<!-- change the namespace declaration for n0 to rsm -->
<xsl:template match="n0:*">
<xsl:element name="rsm:{local-name()}">
<xsl:apply-templates select="*|text()"/>
</xsl:element>
</xsl:template>
<!-- change the namespace declaration for n1 to qdt -->
<xsl:template match="n1:*">
<xsl:element name="qdt:{local-name()}">
<xsl:apply-templates select="*|text()"/>
</xsl:element>
</xsl:template>
<!-- change the namespace declaration for n2 to ram -->
<xsl:template match="n2:*">
<xsl:element name="ram:{local-name()}">
<xsl:apply-templates select="*|text()"/>
</xsl:element>
</xsl:template>
<!-- change the namespace declaration for n3 to udt -->
<xsl:template match="n3:*">
<xsl:element name="udt:{local-name()}">
<xsl:apply-templates select="*|text()"/>
</xsl:element>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
And it's nearly working! But for...
<n2:IssueDateTime>
<n3:DateTimeString format="102">20201117</n3:DateTimeString>
</n2:IssueDateTime>
I get
<ram:IssueDateTime>
<udt:DateTimeString>20201117</udt:DateTimeString>
</ram:IssueDateTime>
So for some reason, the format attribute is being lost.
2021 Jan 06 10:20 AM
@* must be added for attributes:
...
<xsl:apply-templates select="*|text()|@*"/>
...
<xsl:template match="@*">
<xsl:attribute name="{local-name()}"><xsl:value-of select="."/></xsl:attribute>
</xsl:template>
<xsl:template match="@n0:*">
<xsl:attribute name="rsm:{local-name()}"><xsl:value-of select="."/></xsl:attribute>
</xsl:template>
<xsl:template match="@n2:*">
<xsl:attribute name="ram:{local-name()}"><xsl:value-of select="."/></xsl:attribute>
</xsl:template>
...
2021 Jan 06 11:09 AM
Brilliant!
I finally constructed this:
<?sap.transform xslt?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:n0="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
xmlns:n1="urn:un:unece:uncefact:data:standard:QualifiedDataType:100"
xmlns:n2="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
xmlns:n3="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"
xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
xmlns:qdt="urn:un:unece:uncefact:data:standard:QualifiedDataType:100"
xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"
version="2.0">
<xsl:output encoding="UTF-8"
indent="yes"
method="xml"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates select="*|text()|@*"/>
</xsl:template>
<xsl:template match="@*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<!-- change the namespace declaration for n0 to rsm -->
<xsl:template match="n0:*">
<xsl:element name="rsm:{local-name()}">
<xsl:apply-templates select="*|text()|@*"/>
</xsl:element>
</xsl:template>
<!-- change the namespace declaration for n1 to qdt -->
<xsl:template match="n1:*">
<xsl:element name="qdt:{local-name()}">
<xsl:apply-templates select="*|text()|@*"/>
</xsl:element>
</xsl:template>
<!-- change the namespace declaration for n2 to ram -->
<xsl:template match="n2:*">
<xsl:element name="ram:{local-name()}">
<xsl:apply-templates select="*|text()|@*"/>
</xsl:element>
</xsl:template>
<!-- change the namespace declaration for n3 to udt -->
<xsl:template match="n3:*">
<xsl:element name="udt:{local-name()}">
<xsl:apply-templates select="*|text()|@*"/>
</xsl:element>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
I've checked in both ZUGFeRD community and gefeg/invoice.fans and it works. It's all valid.
| User | Count |
|---|---|
| 3 | |
| 2 | |
| 2 | |
| 1 | |
| 1 | |
| 1 | |
| 1 | |
| 1 | |
| 1 | |
| 1 |