views:

435

answers:

3

I am working with Linq to Xml to manipulate openXml documents. More precisely I am trying to read and write to the documents custom properties. I am currently having a problem appending a prefix onto an XElement. My code looks like:

Dim main as XNameSpace = "http://schemas.openxmlformats.org/officeDocument/2006/custom-properties"

Dim vt as XNameSpace = "http://schemas.openxmlformats.org/officeDocument2006/docPropsVTypes"

Dim props as XElement = cXDoc.Element(main + "Properties"
        props.Add(New XElement(main + "property"), _
                               New XAttribute("fmtid", formatId), _
                               New XAttribute("pid", pid + 1), _
                               New XAttribute("name", "test"), _
                                    New XElement(vt + "lpwstr", "test value")) _
                 )

The Xml contained in props before the add is :

<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/custom-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes" />

The Xml after the props.add method() call is:

   <Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/custom-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"&gt;
  <property fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}" pid="2" name="test">
    <lpwstr xmlns="http://schemas.openxmlformats.org/officeDocument2006/docPropsVTypes"&gt;test value</lpwstr>
  </property>
</Properties>

Within the property element I should be getting

<vt:lpwstr>test value</vt:lpwstr>

but just can't get this to happen. I don't want the xmlns attribute for this element here either. I think I somehow need to get the map the vt XNameSpace back to the namespace declaration in the root element "Properties". Does anyone have any suggestions?

+1  A: 

This page might point you in the right direction http://msdn.microsoft.com/en-us/library/bb387069.aspx

jarrett
Hi Jarret,Thanks for the link. I have looked at this link and many other similar links. This link related to C# in particular. Now while you'd expect that the output from the Linq to Xml would be exactly the same in C# and Vb.Net it isn't. Vb.Ne tries to work out if you need the namespace declaration for you. In my case I don't, but I don't know how to get the control back.
Andrew
+1  A: 

The way I have found to control where the namepaces are declared is to use Xml Literals. I also have to recreate the document from scratch and copy any existing information from the old document into my newly created document which isn't ideal. There is also a bug in the example above which is enough to get any of the Office Documents to corrupt after running the code.

Dim vt as XNameSpace = "http://schemas.openxmlformats.org/officeDocument2006/docPropsVTypes"

Should read

Dim vt as XNameSpace = "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"
Andrew
Welcome to StackOverflow Andrew! It is OK for you to answer your own question, but when you're just providing more information, it would be better to edit your question.
Dennis Palmer
+1  A: 

At some place in the XElement, you'll need the prefix defined. Here's how to do it by putting the vt xmlns at the top, by adding it as an XAttribute: New XAttribute(XNamespace.Xmlns + "vt", "http://schemas.openxmlformats.org/officeDocument2006/docPropsVTypes")

Dim main As XNamespace = "http://schemas.openxmlformats.org/officeDocument/2006/custom-properties"
Dim vt As XNamespace = "http://schemas.openxmlformats.org/officeDocument2006/docPropsVTypes"
Dim formatId = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"
Dim pid = "2"
Dim props As New XElement(main + "Properties", New XAttribute(XNamespace.Xmlns + "vt", "http://schemas.openxmlformats.org/officeDocument2006/docPropsVTypes"))
props.Add(New XElement(main + "property"), _
                       New XAttribute("fmtid", formatId), _
                       New XAttribute("pid", pid + 1), _
                       New XAttribute("name", "test"), _
                            New XElement(vt + "lpwstr", "test value"))

XML Literals and global namespaces may be easier, but you'll still need vt listed in the XML at a parent level. Here's an XML Literals example (remember to put both Imports statements at the top of the class/module, above everything else):

Imports <xmlns="http://schemas.openxmlformats.org/officeDocument/2006/custom-properties"&gt;
Imports <xmlns:vt="http://schemas.openxmlformats.org/officeDocument2006/docPropsVTypes"&gt;
    Sub GetXml()
        Dim formatId = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"
        Dim pid = "2"
        Dim props2 = <Properties>
                         <property fmtid=<%= formatId %> pid=<%= pid + 1 %> name="test">
                             <vt:lpwstr>test value</vt:lpwstr>
                         </property>
                     </Properties>
        MsgBox(props2.ToString)
    End Sub
Otaku