xmlstarlet for inserting string under xml fields

48 views Asked by At

We have a policy.xml file as below and need to insert a string like below under each session of the xml file under (inbound or backend or outbound) just under <base /> depending on the condition from shell script,

Sample policy.xml

<policies>
    <inbound>
      <base />
    </inbound>
    <backend>
      <base />
    </backend>
    <outbound>
      <base />
    </outbound>
    <on-error>
      <base />
    </on-error>
</policies>

String want to add

<rate-limit-by-key calls="xx" renewal-period="xx" counter-key="@(Regex.Match(context.x.y.GetValueOrDefault("xxxxxxx",""), @"^[.x-y]*")?.Value)" />`

we tried to use xmlstarlet for this purpose, but couldn't achieve what we are looking for

for example to import the string under [policies/inbound/base]

xmlstarlet edit --subnode "//policies/inbound/" xxxxxxxxxxxxxxxx policy.xml > custompolicy.xml

our Script

#!/bin/bash
################Variables##################
source parse_yaml.sh
eval $(parse_yaml input2.yaml policy)

ipfilter="<rate-limit-by-key calls="xxx" renewal-period="xx" counter-key="@(Regex.Match(context.x.y.GetValueOrDefault("xxxxxxx",""), @"^[.x-y]*")?.Value)" />"

echo ".............Eval Result..............................."
for f in $policy_ ; do eval echo \$f \$${f}_ ; done
echo "............Eval Result................................"
echo " ********policy is ************ "

for f in $policy_ ; do
  if [[ $(eval echo \$${f}_name) == "ipfilter" ]]; then
    echo " given policy name is ipfilter "
    if [[ $(eval echo \$${f}_scope) == "api" ]]; then
    echo "decided the scope of Ipfilter policy as api "
    fi
    for g in $(eval echo \$${f}_apis_); do
     echo " this policy will be applied to apis, $g"
     for h in $(eval echo \$${g}_session_); do
     echo " this policy will be applied to api, $g under sesion $h "
     if [[ $(eval echo \$${g}_session_) == "inbound" ]]; then
     echo "add the add Ipfilter string to the inbound session of xml"
     xmlstarlet edit --subnode "//policies/ibound/" xxxxxxxxxxxxxxxx policy.xml > custompolicy.xml
     fi
     done

Expected Output for the custom policy file generated for the condition succeeded for "inbound"

<policies>
    <inbound>
      <base />
      <rate-limit-by-key calls="xxx" renewal-period="xx" counter-key="@(Regex.Match(context.x.y.GetValueOrDefault("xxxxxxx",""), @"^[.x-y]*")?.Value)" />
    </inbound>
    <backend>
      <base />
    </backend>
    <outbound>
      <base />
    </outbound>
    <on-error>
      <base />
    </on-error>
</policies>
1

There are 1 answers

2
Yitzhak Khabinsky On

Here it is via XSLT.

The XSLT is using a so called Identity Transform pattern.

Also will this make a duplicate entry, if the string is already present?

XPath predicate [not(rate-limit-by-key)] is preventing duplicate entry.

You just need to know how to launch XSLT transformation in your environment.

XmlStarlet Reference - Transforming XML documents

input XML

<policies>
    <inbound>
        <base/>
    </inbound>
    <backend>
        <base/>
    </backend>
    <outbound>
        <base/>
    </outbound>
    <on-error>
        <base/>
    </on-error>
</policies>

XSLT 1.0

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" omit-xml-declaration="yes"
               encoding="UTF-8" indent="yes"/>
   <xsl:strip-space elements="*"/>

    <!--Identity transform-->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="inbound[not(rate-limit-by-key)] | backend[not(rate-limit-by-key)] | outbound[not(rate-limit-by-key)]">
        <xsl:copy>
            <xsl:copy-of select="*"/>
            <rate-limit-by-key calls="xx" renewal-period="xx" counter-key='@(Regex.Match(context.x.y.GetValueOrDefault("xxxxxxx",""), @"^[.x-y]*")?.Value)' />
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Output XML

<policies>
  <inbound>
    <base/>
    <rate-limit-by-key calls="xx" renewal-period="xx" counter-key='@(Regex.Match(context.x.y.GetValueOrDefault("xxxxxxx",""), @"^[.x-y]*")?.Value)'/>
  </inbound>
  <backend>
    <base/>
    <rate-limit-by-key calls="xx" renewal-period="xx" counter-key='@(Regex.Match(context.x.y.GetValueOrDefault("xxxxxxx",""), @"^[.x-y]*")?.Value)'/>
  </backend>
  <outbound>
    <base/>
    <rate-limit-by-key calls="xx" renewal-period="xx" counter-key='@(Regex.Match(context.x.y.GetValueOrDefault("xxxxxxx",""), @"^[.x-y]*")?.Value)'/>
  </outbound>
  <on-error>
    <base/>
  </on-error>
</policies>