1

I want to be able to parse some values in browser-side (no Node etc.) Javascript from an XML of the following format:

<list>
<dict>
    <key>BuildMachineOSBuild</key>
    <string>16B2555</string>
    <key>CFBundleDevelopmentRegion</key>
    <string>en</string>
    <key>CFBundleDisplayName</key>
    <string>App AutoUpdate</string>
    <key>CFBundleExecutable</key>
    <string>App AutoUpdate</string>
    <key>CFBundleIconFile</key>
    <string>AppIcon</string>
    <key>CFBundleIdentifier</key>
    <string>com.app.autoupdate2</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleName</key>
    <string>App AutoUpdate</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleShortVersionString</key>
    <string>3.8.1</string>
    <key>CFBundleSignature</key>
    <string>MSau</string>
    <key>CFBundleSupportedPlatforms</key>
    <array>
        <string>MacOSX</string>
    </array>
    <key>CFBundleVersion</key>
    <string>3.8.16112200</string>
    <key>DTCompiler</key>
    <string>com.apple.compilers.llvm.clang.1_0</string>
    <key>DTPlatformBuild</key>
    <string>8B62</string>
    <key>DTPlatformVersion</key>
    <string>GM</string>
    <key>DTSDKBuild</key>
    <string>16B2649</string>
    <key>DTSDKName</key>
    <string>macosx10.12</string>
    <key>DTXcode</key>
    <string>0810</string>
    <key>DTXcodeBuild</key>
    <string>8B62</string>
    <key>LSHasLocalizedDisplayName</key>
    <true/>
    <key>LSMinimumSystemVersion</key>
    <string>10.10</string>
    <key>LSRequiresNativeExecution</key>
    <true/>
    <key>MbuLoggingFullLegacyUpload</key>
    <true/>
    <key>AppBuildNumber</key>
    <string>161122</string>
    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>
    <key>NSMainNibFile</key>
    <string>SAUMainWindow</string>
    <key>NSPrincipalClass</key>
    <string>SAUApplication</string>
</dict>
</list>

Specifically, I want to be able to parse the values of CFBundleIdentifier and CFBundleShortVersionString. Suppose, I want to store them in an object metadata = {id, version}.

I learned from Parsing properties from certain nodes in XML using Javascript? how to parse data from XML nodes. The problem now is that unlike the abovementioned question, this format of XML doesn't store the values between the tags like so <CFBundleIdentifier>com.app.autoupdate2</CFBundleIdentifier>, but, instead, the name of the property I want to parse is between <key> tags and the property value follows after that--between the <string> tags.

If it can be done using XPATH, how would I form the query in this situation?

1
  • Use the following-sibling axis e.g. //key[. = 'CFBundleIdentifier']/following-sibling::*[1] gives the element node following that particular key element. Commented Aug 13, 2021 at 16:28

2 Answers 2

2

Try something along these lines:

#this assumes your xml string is assigned to xmldoc

let domElement = new DOMParser().parseFromString(xmldoc,'text/xml');
identifiers = ["CFBundleIdentifier","CFBundleShortVersionString"]
for (let id of identifiers){
  exp = `//key[text()="${id}"]/following-sibling::string[1]/text()`;
  let target = domElement.evaluate(exp, domElement, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  console.log(target.snapshotItem(0).nodeValue);    
}

Output:

"com.app.autoupdate2"
"3.8.1"

You can then assign these to whatever variables you want.

Sign up to request clarification or add additional context in comments.

Comments

2

Using JavaScript and the browser's XPath 1.0 evaluate method:

const xml = `<list>
<dict>
    <key>BuildMachineOSBuild</key>
    <string>16B2555</string>
    <key>CFBundleDevelopmentRegion</key>
    <string>en</string>
    <key>CFBundleDisplayName</key>
    <string>App AutoUpdate</string>
    <key>CFBundleExecutable</key>
    <string>App AutoUpdate</string>
    <key>CFBundleIconFile</key>
    <string>AppIcon</string>
    <key>CFBundleIdentifier</key>
    <string>com.app.autoupdate2</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleName</key>
    <string>App AutoUpdate</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleShortVersionString</key>
    <string>3.8.1</string>
    <key>CFBundleSignature</key>
    <string>MSau</string>
    <key>CFBundleSupportedPlatforms</key>
    <array>
        <string>MacOSX</string>
    </array>
    <key>CFBundleVersion</key>
    <string>3.8.16112200</string>
    <key>DTCompiler</key>
    <string>com.apple.compilers.llvm.clang.1_0</string>
    <key>DTPlatformBuild</key>
    <string>8B62</string>
    <key>DTPlatformVersion</key>
    <string>GM</string>
    <key>DTSDKBuild</key>
    <string>16B2649</string>
    <key>DTSDKName</key>
    <string>macosx10.12</string>
    <key>DTXcode</key>
    <string>0810</string>
    <key>DTXcodeBuild</key>
    <string>8B62</string>
    <key>LSHasLocalizedDisplayName</key>
    <true/>
    <key>LSMinimumSystemVersion</key>
    <string>10.10</string>
    <key>LSRequiresNativeExecution</key>
    <true/>
    <key>MbuLoggingFullLegacyUpload</key>
    <true/>
    <key>AppBuildNumber</key>
    <string>161122</string>
    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>
    <key>NSMainNibFile</key>
    <string>SAUMainWindow</string>
    <key>NSPrincipalClass</key>
    <string>SAUApplication</string>
</dict>
</list>`;

const xmlDoc = new DOMParser().parseFromString(xml, 'application/xml');

console.log(["CFBundleIdentifier","CFBundleShortVersionString"].map(key => { return { [key] : xmlDoc.evaluate(`string(//key[. = '${key}']/following-sibling::*[1])`, xmlDoc, null, XPathResult.STRING_TYPE, null).stringValue };}));

Using JavaScript and Saxon-JS 2 to exploit XPath 3.1:

const xml = `<list>
<dict>
    <key>BuildMachineOSBuild</key>
    <string>16B2555</string>
    <key>CFBundleDevelopmentRegion</key>
    <string>en</string>
    <key>CFBundleDisplayName</key>
    <string>App AutoUpdate</string>
    <key>CFBundleExecutable</key>
    <string>App AutoUpdate</string>
    <key>CFBundleIconFile</key>
    <string>AppIcon</string>
    <key>CFBundleIdentifier</key>
    <string>com.app.autoupdate2</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleName</key>
    <string>App AutoUpdate</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleShortVersionString</key>
    <string>3.8.1</string>
    <key>CFBundleSignature</key>
    <string>MSau</string>
    <key>CFBundleSupportedPlatforms</key>
    <array>
        <string>MacOSX</string>
    </array>
    <key>CFBundleVersion</key>
    <string>3.8.16112200</string>
    <key>DTCompiler</key>
    <string>com.apple.compilers.llvm.clang.1_0</string>
    <key>DTPlatformBuild</key>
    <string>8B62</string>
    <key>DTPlatformVersion</key>
    <string>GM</string>
    <key>DTSDKBuild</key>
    <string>16B2649</string>
    <key>DTSDKName</key>
    <string>macosx10.12</string>
    <key>DTXcode</key>
    <string>0810</string>
    <key>DTXcodeBuild</key>
    <string>8B62</string>
    <key>LSHasLocalizedDisplayName</key>
    <true/>
    <key>LSMinimumSystemVersion</key>
    <string>10.10</string>
    <key>LSRequiresNativeExecution</key>
    <true/>
    <key>MbuLoggingFullLegacyUpload</key>
    <true/>
    <key>AppBuildNumber</key>
    <string>161122</string>
    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>
    <key>NSMainNibFile</key>
    <string>SAUMainWindow</string>
    <key>NSPrincipalClass</key>
    <string>SAUApplication</string>
</dict>
</list>`;


console.log(["CFBundleIdentifier","CFBundleShortVersionString"].map(key => SaxonJS.XPath.evaluate(`parse-xml($xml)//key[. = $key] ! map { $key : following-sibling::*[1]/string() }`, [], { params : { xml : xml, key : key } })));
<script src="https://www.saxonica.com/saxon-js/documentation/SaxonJS/SaxonJS2.rt.js"></script>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.