How can I save an MSXML2.DomDocument with indenting? (I think it uses MXXMLWriter)
Asked Answered
C

4

9

I have an instance of MSXML2.DomDocument.

I wave to save it, with indenting.

This code works, but does not indent:

var dom = new ActiveXObject("MSXML2.DomDocument");
// fiddle with dom here
dom.save(filename);

I think I can use an MXXMLWriter object to inject indenting.

How?

Crystallite answered 21/6, 2012 at 18:11 Comment(0)
C
7

This oughta do it.

function saveDomWithIndent(dom, filename) {
    var writer =  new ActiveXObject("MSXML2.MXXMLWriter"),
        reader = new ActiveXObject("MSXML2.SAXXMLReader"),
        fso = new ActiveXObject("Scripting.FileSystemObject"),
        textStream = fso.CreateTextFile(filename, true);
    writer.indent = true;
    writer.omitXMLDeclaration = true;
    reader.contentHandler = writer;
    reader.parse(dom);
    textStream.Write(writer.output);
    textStream.Close();
}

Use it like this:

var root, node, newnode, 
    dom = new ActiveXObject("MSXML2.DOMDocument.6.0");
dom.async = false;
dom.resolveExternals = false;
dom.load(fullpath);
root = dom.documentElement;
node = root.selectSingleNode("/root/node1");
if (node !== null) {
    newnode = dom.createElement('node2');
    newnode.text = "hello";
    root.appendChild(newnode);
    saveDomWithIndent(dom, fullpath);
}

I could not figure out how to adjust the indent level. It always indents with a tab.

Crystallite answered 21/6, 2012 at 19:24 Comment(0)
L
1

There is another way to prettifying xml outputs, plus you can adjust the indent level manually : XSL.

var adSaveCreateOverWrite = 2
var Indent = new ActiveXObject("MSXML2.DomDocument");
    Indent.async = false;
    Indent.resolveExternals = false;
    Indent.load("indent.xsl");
var Doc = new ActiveXObject("MSXML2.DomDocument");
    Doc.async = false;
    Doc.resolveExternals = false;
    Doc.load("dirty.xml");
with(new ActiveXObject("ADODB.Stream")){
    Charset = "utf-8";
    Open();
    WriteText(Doc.transformNode(Indent));
    SaveToFile("pretty.xml", adSaveCreateOverWrite);
    Close();
}

indent.xsl

<?xml version="1.0" encoding="ISO-8859-15"?>
<!-- http://x443.wordpress.com/2011/page/34/ -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml"/>

  <xsl:template match="@*">
    <xsl:copy/>
  </xsl:template>

  <xsl:template match="text()">
    <xsl:value-of select="normalize-space(.)" />
  </xsl:template>

  <xsl:template match="*">
    <xsl:param name="indent" select="''"/>
    <xsl:text>&#xa;</xsl:text>
    <xsl:value-of select="$indent" />
    <xsl:copy>
      <xsl:apply-templates select="@*|*|text()">
        <xsl:with-param name="indent" select="concat($indent, '  ')"/>
      </xsl:apply-templates>
    </xsl:copy>
    <xsl:if test="count(../*)>0 and ../*[last()]=.">
      <xsl:text>&#xa;</xsl:text>
      <xsl:value-of select="substring($indent,3)" />
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>
Loony answered 29/6, 2012 at 17:23 Comment(4)
nice. This leads to the followon question - can I use a builtin XSL, one of the XSL sheets embedded in msxml6.dll, to perform the indentation? I suppose it would be necessary to programmatically extract the XSL then apply it.Crystallite
@Crystallite Sorry, I did not understand exactly what you mean. Actually, I'm using FreeThreadedDomDocument instance as a static object which stored in Application scope (initialized on Application_OnStart event) with Classic ASP. This prevents recurring transactions for each transform.Loony
I know this is a stale comment stream now, but... to explain what I meant earlier: I think msxml includes at least one builtin XSLT. see #9463902 The reason I ask is, it would be simpler if I could use a builtin xslt rather than embedding my own custom sheet.Crystallite
@Crystallite Sorry, I finally understand. And yes, it would be great. Thanks!Loony
C
1

A bit overcomplicated ??
Did not work for me (in WSH / cscript) - error and empty file.
Then found you can use save method - my new filter out VS (empty) C++ filters script

var arg = WScript.arguments(0);
var doc = readFile(arg);
var xml = doc.documentElement.firstChild;
do {
    var filters = {};
    while (xml && xml.nodeName != "ItemGroup") xml = xml.nextSibling;
    if (!xml) break;
    var xml2 = xml.firstChild;
    while (xml2 && (xml2.nodeName == "ClInclude" || xml2.nodeName == "ClCompile"))
    {
        var path = xml2.attributes.getNamedItem("Include").nodeValue;
        var filter = xml2.firstChild;
        if (filter != null) {
            filter = filter.text;
        }
        if (filters[path])
        { //WScript.Echo(path + " had " + filter);
            if (!filter)
            {
                var prev = xml2.previousSibling;
                xml.removeChild(xml2);
                xml2 = prev;
            }
        } else {
            if (filter != null) {
                filters[path] = filter;
            }
        }
        xml2 = xml2.nextSibling;
    }
    xml = xml.nextSibling;
} while (xml);
doc.save(arg);

function readFile(filename)
{
    var xml = new ActiveXObject("Msxml2.DOMDocument.6.0");  
    xml.async = false;
    xml.resolveExternals = false;
    xml.async = false;
    xml.load(arg);
    return xml;
}
Curkell answered 26/1, 2022 at 9:10 Comment(0)
F
0

If you don't want to use xsl, you could just insert vbcrlfs. Every ">" should follow vbcrlf, except when ">" is followed by a number. Then create new xml file from that string - now it has new lines and indents. msaccess vba:

Dim objDom As DOMDocument
Set objDom = CreateObject("MSXML2.DOMDocument")

create document with objDom.append, then play with string(my xml had just numbers):

ss = objectDom.XML
For i = 1 To Len(ss)
c = Mid(ss, i, 1)
    If InStr(1, Mid(ss, i, 1), ">") > 0 Then
        a = Asc(Mid(ss, i + 1, 1))
        If a < 48 Or a > 57 Then
            ss1 = Mid(ss, 1, i)
            ss2 = Mid(ss, i + 1, Len(ss))
            ss = Mid(ss, 1, i) & vbCrLf & Mid(ss, i + 1, Len(ss))
        End If
    End If

Next i

objDom.loadXML ss
objDom.Save (file_path)
Fibrous answered 24/6, 2014 at 8:42 Comment(1)
The question is about js (var vs dim)... in vba consider using option explicit. The following was helpful to me, in case someone else is looking for it: vb-helper.com/howto_formatted_xml_document.htmlSteve

© 2022 - 2024 — McMap. All rights reserved.