Home > Net >  Insert a line xml file by using XMLStart
Insert a line xml file by using XMLStart

Time:10-27

Hi I've the following file.

<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="" xmlns:uap="" IgnorableNamespaces="">
  <Identity Name="xxxxxxxxxx" Publisher="CN=$username$" Version="xxxx"/>
  <mp:PhoneIdentity PhoneProductId="xxxxxxxxxxx" PhonePublisherId=""/>
  <Properties>
    <DisplayName>xxxxxxxxxx</DisplayName>
    <PublisherDisplayName>xxxxxxxx</PublisherDisplayName>
    <Logo>images\StoreLogo.png</Logo>
    <Description>A sample Apache Cordova application that responds to the deviceready event.</Description>
  </Properties>
  <Dependencies>
    <TargetDeviceFamily MaxVersionTested="" MinVersion="" Name="Windows.Universal"/>
  </Dependencies>
  <Resources>
    <Resource Language="x-generate"/>
  </Resources>
  <Applications>
    <Application Id="" StartPage="">
      </Application>
  </Applications>
  <Capabilities>
    <Capability Name="internetClient"/>
    <DeviceCapability Name="webcam"/>
    <DeviceCapability Name="location"/>
  </Capabilities>
</Package>

I wanted to insert under the Capabilities element. So, the output should look like

<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="" xmlns:uap="" IgnorableNamespaces="">
  <Identity Name="xxxxxxxxxx" Publisher="CN=$username$" Version="xxxx"/>
  <mp:PhoneIdentity PhoneProductId="xxxxxxxxxxx" PhonePublisherId=""/>
  <Properties>
    <DisplayName>xxxxxxxxxx</DisplayName>
    <PublisherDisplayName>xxxxxxxx</PublisherDisplayName>
    <Logo>images\StoreLogo.png</Logo>
    <Description>A sample Apache Cordova application that responds to the deviceready event.</Description>
  </Properties>
  <Dependencies>
    <TargetDeviceFamily MaxVersionTested="" MinVersion="" Name="Windows.Universal"/>
  </Dependencies>
  <Resources>
    <Resource Language="x-generate"/>
  </Resources>
  <Applications>
    <Application Id="" StartPage="">
      </Application>
  </Applications>
  <Capabilities>
    <Capability Name="internetClient"/>
    <Capability Name="privateNetworkClientServer"/>
    <DeviceCapability Name="webcam"/>
    <DeviceCapability Name="location"/>
  </Capabilities>
</Package>

To achieve the above I'm using XMLStarlet as below.

xmlstarlet ed \
  -a '/Package/Capabilities' -t elem -n Capability \
  -i '/Package/Capabilities/Capability' -t attr -n Name -v privateNetworkClientServer \
<inputfile >outputfile

The output I'm getting is the same without any modifications. Am I doing anything silly.

CodePudding user response:

If I strip out all the namespace-related stuff, then

xmlstarlet ed \
    -a '/Package/Capabilities/Capability[last()]' -t elem -n 'Capability' \
    -a '/Package/Capabilities/Capability[last()]' -t attr -n Name -v privateNetworkClientServer \
  inputfile.no_namespace

adds the node after the last Capability:

<?xml version="1.0" encoding="utf-8"?>
<Package IgnorableNamespaces="">
  ...
  <Capabilities>
    <Capability Name="internetClient"/>
    <Capability Name="privateNetworkClientServer"/>
    <DeviceCapability Name="webcam"/>
    <DeviceCapability Name="location"/>
  </Capabilities>
</Package>

However, with your given XML, xmlstarlet spits out errors:

$ xmlstarlet ... inputfile
inputfile:2.93: xmlns:mp: Empty XML namespace is not allowed
ns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp=""
                                                                               ^
inputfile:2.106: xmlns:uap: Empty XML namespace is not allowed
hemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="" xmlns:uap=""
                                                                               ^
inputfile:4.69: Namespace prefix mp on PhoneIdentity is not defined
  <mp:PhoneIdentity PhoneProductId="xxxxxxxxxxx" PhonePublisherId=""/>
                                                                    ^
[original XML here]

You're dealing with invalid XML, and xmlstarlet is not very forgiving.

CodePudding user response:

The target node exists in the default namespace so adjust your XPath expression:

xmlstarlet edit \
  -a '/_:Package/_:Capabilities/_:Capability[last()]' -t elem -n 'Capability' \
  -s '$prev' -t attr -n Name -v 'privateNetworkClientServer' \
file.xml
  • using the _ (underscore) shortcut since the default namespace is declared in the root element (option --doc-namespace being in effect)
  • the $prev (aka $xstar:prev) variable refers to the node created by the most recent -i (--insert), -a (--append), or -s (--subnode); examples of $prev are given in doc/xmlstarlet.txt.

Due to a bug in xmlstarlet edit (see this) the $prev back reference is required when referencing a newly created element which is not in the null namespace.

Since xmlstarlet edit (unlike xmlstarlet select) isn't an XSLT processor it'll accept stuff -- such as xmlns:mp="" -- XSLT processors will reject as non-XML.

  • Related