{"id":3923,"date":"2008-10-06T18:25:00","date_gmt":"2008-10-06T18:25:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/vbteam\/2008\/10\/06\/vb-xml-cookbook-recipe-6-writing-an-xslt-transform-in-vb-doug-rothaus\/"},"modified":"2024-07-05T14:07:22","modified_gmt":"2024-07-05T21:07:22","slug":"vb-xml-cookbook-recipe-6-writing-an-xslt-transform-in-vb-doug-rothaus","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/vbteam\/vb-xml-cookbook-recipe-6-writing-an-xslt-transform-in-vb-doug-rothaus\/","title":{"rendered":"VB XML Cookbook, Recipe 6: Writing an XSLT Transform in VB (Doug Rothaus)"},"content":{"rendered":"<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">Most XSLT programmers are familiar with this XSLT transform to copy an XML file. <\/font><\/p>\n<p class=\"MsoNormal\"><span>&lt;?<\/span><span>xml<\/span><span> <\/span><span>version<\/span><span>=<\/span><span>&#8220;<span>1.0<\/span>&#8220;<span> <\/span><span>encoding<\/span><span>=<\/span>&#8220;<span>utf-8<\/span>&#8220;<span>?&gt;<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span>&lt;<\/span><span>xsl:stylesheet<\/span><span> <\/span><span>version<\/span><span>=<\/span><span>&#8220;<span>1.0<\/span>&#8220;<span> <\/span><span>xmlns:xsl<\/span><span>=<\/span>&#8220;<span>http:\/\/www.w3.org\/1999\/XSL\/Transform<\/span>&#8220;<span>&gt;<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>&lt;<\/span><span>xsl:output<\/span><span> <\/span><span>method<\/span><span>=<\/span><span>&#8220;<span>xml<\/span>&#8220;<span> <\/span><span>indent<\/span><span>=<\/span>&#8220;<span>yes<\/span>&#8220;<span>\/&gt;<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>&lt;<\/span><span>xsl:template<\/span><span> <\/span><span>match<\/span><span>=<\/span><span>&#8220;<span>@* | node()<\/span>&#8220;<span>&gt;<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>&lt;<\/span><span>xsl:copy<\/span><span>&gt;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>&lt;<\/span><span>xsl:apply-templates<\/span><span> <\/span><span>select<\/span><span>=<\/span><span>&#8220;<span>@* | node()<\/span>&#8220;<span>\/&gt;<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>&lt;\/<\/span><span>xsl:copy<\/span><span>&gt;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>&lt;\/<\/span><span>xsl:template<\/span><span>&gt;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>&lt;\/<\/span><span>xsl:stylesheet<\/span><span>&gt;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">This XSLT is commonly used for identity transforms as it allows you to copy an entire XML document and &ldquo;touch&rdquo; each XML node and attribute. If you add a matching template, then you can transform just that attribute or node that has a match in place. Unmatched nodes and attributes are simply copied.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">We can also do this in Visual Basic with XML Literals (including LINQ to XML and XML Axis Properties). Our VB code will allow us to &ldquo;touch&rdquo; each node or element by recursively navigating through an XML document based on the following pseudo-code:<\/font><\/p>\n<p class=\"MsoNormal\"><i><font size=\"3\"><font face=\"Calibri\">Starting with the root element, perform the following whenever you encounter a node <\/p>\n<p><\/font><\/font><\/i><\/p>\n<p class=\"MsoNormal\"><i><font size=\"3\"><font face=\"Calibri\">If the\n node is an element<\/p>\n<p><\/font><\/font><\/i><\/p>\n<p class=\"MsoNormal\"><i><font size=\"3\"><font face=\"Calibri\">If the element has attributes, transform or copy each attribute<\/p>\n<p><\/font><\/font><\/i><\/p>\n<p class=\"MsoNormal\"><i><font size=\"3\"><font face=\"Calibri\">If the element has child nodes, transform or copy each node<\/p>\n<p><\/font><\/font><\/i><\/p>\n<p class=\"MsoNormal\"><i><font size=\"3\"><font face=\"Calibri\">If the node is text, transform or copy the text<\/p>\n<p><\/font><\/font><\/i><\/p>\n<p class=\"MsoNormal\"><i><font size=\"3\"><font face=\"Calibri\">If the node is CData, transform or copy the CData<\/p>\n<p><\/font><\/font><\/i><\/p>\n<p class=\"MsoNormal\"><i><font size=\"3\"><font face=\"Calibri\">If the node is a comment, transform or copy the comment<\/p>\n<p><\/font><\/font><\/i><\/p>\n<p class=\"MsoNormal\"><i><font size=\"3\"><font face=\"Calibri\">If the node is a processing instruction, transform or copy the processing instruction<\/p>\n<p><\/font><\/font><\/i><\/p>\n<p class=\"MsoNormal\">\n<p><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">For this cookbook entry, we&rsquo;ll create an abstract (<b>MustInherit<\/b>) base class that performs this pseudo-coded recursive navigation of an XML document. We can then create a class that inherits from that base class to perform specific transforms. First, we&rsquo;ll create the abstract class and the &ldquo;starting point,&rdquo; a function called <\/font><span>Transform<\/span><font face=\"Calibri\" size=\"3\"> that takes the XML document (<b>XDocument<\/b>) to be transformed as input and returns the transformed document.<\/font><\/p>\n<p class=\"MsoNormal\">\n<p><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<\/p>\n<p class=\"MsoNormal\"><span>Public<\/span><span> <span>MustInherit<\/span> <span>Class<\/span> VBXmlTransform<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp; <\/span><span>Public<\/span> <span>Overridable<\/span> <span>Function<\/span> Transform(<span>ByVal<\/span> xmlDoc <span>As<\/span> XDocument) <span>As<\/span> XDocument<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Return<\/span> <span>&lt;?<\/span><span>xml<\/span> <span>version<\/span><span>=<\/span><span>&#8220;<\/span><span>1.0<\/span><span>&#8220;<\/span> <span>encoding<\/span><span>=<\/span><span>&#8220;<\/span><span>utf-8<\/span><span>&#8220;<\/span><span>?&gt;<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>&lt;%=<\/span> ProcessElement(xmlDoc.Root) <span>%&gt;<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp; <\/span><span>End<\/span> <span>Function<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>End Class<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">Next, we add the logic that is called for each XML node (<b>XNode<\/b>) encountered. This includes elements, text, CData, and so on. Our code needs to determine the type of XML node and call the related function to transform or copy the node type and return the result, which is either a copied or transformed node. This method is called <\/font><span>ProcessNode<\/span><font face=\"Calibri\" size=\"3\"> and is shown here.<\/font><\/p>\n<p class=\"MsoNormal\">\n<p><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp; <\/span><span>Public<\/span> <span>Overridable<\/span> <span>Function<\/span> ProcessNode(<span>ByVal<\/span> xmlNode <span>As<\/span> XNode) <span>As<\/span> XNode<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>&#8216; This method ignores DTD (XDocumentType) content.<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Dim<\/span> nodeType = xmlNode.GetType()<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>&#8216; Because XCData inherits from XText, check for the XCData type before checking <\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span>&#8216; for XText.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>If<\/span> nodeType <span>Is<\/span> <span>GetType<\/span>(XCData) <span>Then<\/span> <span>Return<\/span> ProcessCData(xmlNode)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>If<\/span> nodeType <span>Is<\/span> <span>GetType<\/span>(XText) <span>Then<\/span> <span>Return<\/span> ProcessText(xmlNode)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>If<\/span> nodeType <span>Is<\/span> <span>GetType<\/span>(XElement) <span>Then<\/span> <span>Return<\/span> ProcessElement(xmlNode)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>If<\/span> nodeType <span>Is<\/span> <span>GetType<\/span>(XComment) <span>Then<\/span> <span>Return<\/span> ProcessComment(xmlNode)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>If<\/span> nodeType <span>Is<\/span> <span>GetType<\/span>(XProcessingInstruction) <span>Then<\/span> <span>Return<\/span> _<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>ProcessProcessingInstruction(xmlNode)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Return<\/span> xmlNode<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp; <\/span><span>End<\/span> <span>Function<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">Next, we can add the strongly-typed functions that process each of the node types as well as attributes. The function to process an element is unique, so we&rsquo;ll leave that out for now and cover that next. The functions to process the other node types and attributes are rather simple. Because the default behavior of the base class is to simply copy a document, each function just returns the input value. The reason that we have created this code is to provide strongly-typed functions that we can override in our inheriting class with specific behavior. Here are the strongly-typed functions (without the <\/font><span>ProcessElement<\/span><font face=\"Calibri\" size=\"3\"> function).<\/font><\/p>\n<p class=\"MsoNormal\">\n<p><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp; <\/span><span>Public<\/span> <span>Overridable<\/span> <span>Function<\/span> ProcessAttribute(<span>ByVal<\/span> xmlAttribute <span>As<\/span> XAttribute) <span>As<\/span> XAttribute<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Return<\/span> xmlAttribute<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp; <\/span><span>End<\/span> <span>Function<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp; <\/span><span>Public<\/span> <span>Overridable<\/span> <span>Function<\/span> ProcessCData(<span>ByVal<\/span> xmlCData <span>As<\/span> XCData) <span>As<\/span> XCData<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Return<\/span> xmlCData<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp; <\/span><span>End<\/span> <span>Function<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp; <\/span><span>Public<\/span> <span>Overridable<\/span> <span>Function<\/span> ProcessText(<span>ByVal<\/span> xmlText <span>As<\/span> XText) <span>As<\/span> XText<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Return<\/span> xmlText<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp; <\/span><span>End<\/span> <span>Function<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp; <\/span><span>Protected<\/span> <span>Overridable<\/span> <span>Function<\/span> ProcessComment(<span>ByVal<\/span> xmlComment <span>As<\/span> XComment) <span>As<\/span> XComment<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Return<\/span> xmlComment<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp; <\/span><span>End<\/span> <span>Function<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp; <\/span><span>Public<\/span> <span>Overridable<\/span> <span>Function<\/span> ProcessProcessingInstruction( _<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>ByVal<\/span> pi <span>As<\/span> XProcessingInstruction) <span>As<\/span> XProcessingInstruction<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><\/p>\n<p>&nbsp;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Return<\/span> pi<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp; <\/span><span>End<\/span> <span>Function<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\">\n<p><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">Now let&rsquo;s look at the <\/font><span>ProcessElement<\/span><font face=\"Calibri\" size=\"3\"> function. Processing elements is unique because elements can have both attributes as well as child nodes. Those attributes and child nodes need to be transformed or copied as well, so we must provide code that calls the <\/font><span>ProcessAttribute<\/span><font face=\"Calibri\" size=\"3\"> function for each attribute, and calls the <\/font><span>ProcessNode<\/span><font face=\"Calibri\" size=\"3\"> function for each child node. We&rsquo;ll encapsulate this code in a function called <\/font><span>CopyElement<\/span><font face=\"Calibri\" size=\"3\">. The <\/font><span>ProcessElement<\/span><font face=\"Calibri\" size=\"3\"> function will look like the other strongly-typed functions, except that it will return a call to the <\/font><span>CopyElement<\/span><font face=\"Calibri\" size=\"3\"> function instead of just the input element. The <\/font><span>CopyElement<\/span><font face=\"Calibri\" size=\"3\"> function uses XML Literals, embedded expressions, and LINQ to XML to create the copy of the XML element as shown here.<\/font><\/p>\n<p class=\"MsoNormal\">\n<p><font face=\"Calibri\" size=\"3\">&nbsp;<\/font><\/p>\n<\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp; <\/span><span>Public<\/span> <span>Overridable<\/span> <span>Function<\/span> ProcessElement(<span>ByVal<\/span> xmlElement <span>As<\/span> XElement) <span>As<\/span> XElement<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp;&nbsp;&nbsp; <\/span><span>Return<\/span> CopyElement(xmlElement)<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><span>&nbsp; <\/span><span>End<\/span> <span>Function<\/p>\n<p><\/span><\/span><\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Most XSLT programmers are familiar with this XSLT transform to copy an XML file. &lt;?xml version=&#8220;1.0&#8220; encoding=&#8220;utf-8&#8220;?&gt; &lt;xsl:stylesheet version=&#8220;1.0&#8220; xmlns:xsl=&#8220;http:\/\/www.w3.org\/1999\/XSL\/Transform&#8220;&gt; &nbsp;&nbsp;&nbsp; &lt;xsl:output method=&#8220;xml&#8220; indent=&#8220;yes&#8220;\/&gt; &nbsp; &nbsp;&nbsp;&nbsp; &lt;xsl:template match=&#8220;@* | node()&#8220;&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:copy&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:apply-templates select=&#8220;@* | node()&#8220;\/&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;\/xsl:copy&gt; &nbsp;&nbsp;&nbsp; &lt;\/xsl:template&gt; &lt;\/xsl:stylesheet&gt; This XSLT is commonly used for identity transforms as it allows you to [&hellip;]<\/p>\n","protected":false},"author":260,"featured_media":8818,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[192,195],"tags":[59,117,163,166],"class_list":["post-3923","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-featured","category-visual-basic","tag-doug-rothaus","tag-orcas","tag-vb-xml-cookbook","tag-vb2008"],"acf":[],"blog_post_summary":"<p>Most XSLT programmers are familiar with this XSLT transform to copy an XML file. &lt;?xml version=&#8220;1.0&#8220; encoding=&#8220;utf-8&#8220;?&gt; &lt;xsl:stylesheet version=&#8220;1.0&#8220; xmlns:xsl=&#8220;http:\/\/www.w3.org\/1999\/XSL\/Transform&#8220;&gt; &nbsp;&nbsp;&nbsp; &lt;xsl:output method=&#8220;xml&#8220; indent=&#8220;yes&#8220;\/&gt; &nbsp; &nbsp;&nbsp;&nbsp; &lt;xsl:template match=&#8220;@* | node()&#8220;&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:copy&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:apply-templates select=&#8220;@* | node()&#8220;\/&gt; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;\/xsl:copy&gt; &nbsp;&nbsp;&nbsp; &lt;\/xsl:template&gt; &lt;\/xsl:stylesheet&gt; This XSLT is commonly used for identity transforms as it allows you to [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/posts\/3923","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/users\/260"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/comments?post=3923"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/posts\/3923\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/media\/8818"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/media?parent=3923"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/categories?post=3923"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/vbteam\/wp-json\/wp\/v2\/tags?post=3923"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}