tags:

views:

55

answers:

1

I need to add a namespace, and to add an attribute to certain nodes. With this input:

<root>
  <Node1>test</Node1>
  <DateTo />
</root>

I want this output:

<my:root xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/2010-07-28T07:33:11"&gt;
  <my:Node1>test</my:Node1>
  <my:DateTo xsi:nil="true"/>
</my:root>

The DateTo node needs to have this attribute set.

I successfully added the namespace with this transform, but cannot get the attribute added.

<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'>
    <xsl:template match='*'>
        <xsl:element name='my:{local-name()}' namespace='http://schemas.microsoft.com/office/infopath/2003/myXSD/2010-07-28T07:33:11' >
            <xsl:apply-templates />
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>"

I get this error "Attribute and namespace nodes cannot be added to the parent element after a text, comment, pi, or sub-element node has already been added." Be grateful for any help here.

+1  A: 

You can try the following to insert the additional attribute:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
                xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
                version='1.0'>
  <xsl:template match='*'>
    <xsl:element name='my:{local-name()}'
                 xmlns:my='http://schemas.microsoft.com/office/infopath/2003/myXSD/2010-07-28T07:33:11'&gt;
      <xsl:if test="not(*) and not(normalize-space())">
        <xsl:attribute name="xsi:nil">
          <xsl:value-of select="true()"/>
        </xsl:attribute>
      </xsl:if>
      <xsl:apply-templates />
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>
0xA3
The namespace part of my code is working fine, it's combining that with adding an attribute to my date elements that I am struggling with. Any tips there?
Graeme
@Graeme: I misunderstood your question, see my updated answer.
0xA3
What does the xsl:if do here? I assumed I would check for the element DateTo and add the attribute to that element.I tried test=".='DateTo'" but that didn't work. Taking out the xsl:if causes the attribute to be added to all elements, I just need it added to the DateTo element...
Graeme
Think I might have it - using <xsl:if test="local-name()='DateTo'>adds the attribute to the desired node.
Graeme
The if-condition checks whether an element is empty and then adds `xsi:nil` to the element. You should add this attribute only if an element is empty, that's why the check is there.
0xA3
OK, now I understand. I didn't realise that xsi:nil meant the element MUST be empty - I thought it was CAN be empty. Everything works as advertised now. Thanks!
Graeme
@0xA3: I think you must correct your answer. This fragment `<Node><Child></Child></Node>` also has an empty string value.
Alejandro
@Alejandro: Corrected.
0xA3
@0xA3: What do you think about `not(node())` test?
Alejandro
@Alejandro: I don't think `not(node())` is correct. `xsi:nil` allows the node to have attributes, but the condition would be false in that case and no `xsi:nil` would be generated although it probably should.
0xA3
@0xA3: `node()` would be expanded to `child::node()`, so it doesn't test for attributes.
Alejandro
@Alejandro: You are correct with your last comment, but still `not(node())` wouldn't handle whitespace correctly in my opinion, a test for `not(normalize-space())` would still be needed.
0xA3
So what is the difference between `test=".=''"` and the new version?
Graeme
@Graeme: The new version checks whether 1) there are no child nodes to the current node, and 2) the current node does not contain text but allows whitespace.
0xA3