I have the following source as XML:
<?xml version="1.0"?>
<report>
<feature tag="Config"/>
<feature tag="Runtime">
<feature tag="Metadata">
<property name="date" value="16.01.2025"/>
<property name="time" value="09:31:34"/>
</feature>
<feature tag="Templates">
<feature tag="Template">
<property name="username" value="myself"/>
<property name="password" value="something"/>
<feature tag="Data sources">
<feature tag="Source">
<property name="name" value="modules"/>
<property name="driver" value="eval"/>
</feature>
<feature tag="Source">
<property name="name" value="Artifact"/>
<property name="driver" value="eval"/>
</feature>
<feature tag="Source">
<property name="name" value="Comments"/>
<property name="driver" value="eval"/>
</feature>
</feature>
</feature>
</feature>
</feature>
</feature>
</report>
I want to modify the value of the driver property (below the feature tag Source), but only if the value of the name property (below the feature tag Source) equals the word "modules".
I tried to use the following function only to extract the feature tags Source. I think it is possible to modify the properties in the way I want in one LINQ command, but I don't know how to formulate this if construct in XPath.
Private Function ModifyXml(ByVal xml As String) As Boolean
Try
Dim xdoc As New XDocument
xdoc = XDocument.Parse(xml)
Dim query As String = "/report/feature[@tag='Runtime']/feature[@tag='Templates']/feature[@tag='Template']/feature[@tag='Data sources']/feature[@tag='Source']"
xdoc.XPathSelectElements(query).ToList()
xdoc.Save("c:\temp\myFile.xml")
Return True
Catch ex As Exception
Return False
End Try
End Function
The result should look like this:
I need a driver name in the properties as value. This driver name depends on the value of the name properties.
<?xml version="1.0"?>
<report>
<feature tag="Config"/>
<feature tag="Runtime">
<feature tag="Metadata">
<property name="date" value="16.01.2025"/>
<property name="time" value="09:31:34"/>
</feature>
<feature tag="Templates">
<feature tag="Template">
<property name="username" value="myself"/>
<property name="password" value="something"/>
<feature tag="Data sources">
<feature tag="Source">
<property name="name" value="modules"/>
<property name="driver" value="somedriver1"/>
</feature>
<feature tag="Source">
<property name="name" value="Artifact"/>
<property name="driver" value="somedriver2"/>
</feature>
<feature tag="Source">
<property name="name" value="Comments"/>
<property name="driver" value="somedriver3"/>
</feature>
</feature>
</feature>
</feature>
</feature>
</feature>
</report>
I have the following source as XML:
<?xml version="1.0"?>
<report>
<feature tag="Config"/>
<feature tag="Runtime">
<feature tag="Metadata">
<property name="date" value="16.01.2025"/>
<property name="time" value="09:31:34"/>
</feature>
<feature tag="Templates">
<feature tag="Template">
<property name="username" value="myself"/>
<property name="password" value="something"/>
<feature tag="Data sources">
<feature tag="Source">
<property name="name" value="modules"/>
<property name="driver" value="eval"/>
</feature>
<feature tag="Source">
<property name="name" value="Artifact"/>
<property name="driver" value="eval"/>
</feature>
<feature tag="Source">
<property name="name" value="Comments"/>
<property name="driver" value="eval"/>
</feature>
</feature>
</feature>
</feature>
</feature>
</feature>
</report>
I want to modify the value of the driver property (below the feature tag Source), but only if the value of the name property (below the feature tag Source) equals the word "modules".
I tried to use the following function only to extract the feature tags Source. I think it is possible to modify the properties in the way I want in one LINQ command, but I don't know how to formulate this if construct in XPath.
Private Function ModifyXml(ByVal xml As String) As Boolean
Try
Dim xdoc As New XDocument
xdoc = XDocument.Parse(xml)
Dim query As String = "/report/feature[@tag='Runtime']/feature[@tag='Templates']/feature[@tag='Template']/feature[@tag='Data sources']/feature[@tag='Source']"
xdoc.XPathSelectElements(query).ToList()
xdoc.Save("c:\temp\myFile.xml")
Return True
Catch ex As Exception
Return False
End Try
End Function
The result should look like this:
I need a driver name in the properties as value. This driver name depends on the value of the name properties.
<?xml version="1.0"?>
<report>
<feature tag="Config"/>
<feature tag="Runtime">
<feature tag="Metadata">
<property name="date" value="16.01.2025"/>
<property name="time" value="09:31:34"/>
</feature>
<feature tag="Templates">
<feature tag="Template">
<property name="username" value="myself"/>
<property name="password" value="something"/>
<feature tag="Data sources">
<feature tag="Source">
<property name="name" value="modules"/>
<property name="driver" value="somedriver1"/>
</feature>
<feature tag="Source">
<property name="name" value="Artifact"/>
<property name="driver" value="somedriver2"/>
</feature>
<feature tag="Source">
<property name="name" value="Comments"/>
<property name="driver" value="somedriver3"/>
</feature>
</feature>
</feature>
</feature>
</feature>
</feature>
</report>
You could use a simple xslt to do the job:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<!-- Identity template to copy the rest of the XML as is -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!--
A template match on with this xpath for the correct attribute to change:
feature[property/@value='modules']/property[@name='driver']/@value
-->
<xsl:template match="feature[property/@value='modules']/property[@name='driver']/@value">
<xsl:attribute name="value">somedriver1</xsl:attribute>
</xsl:template>
<!--
A template match on with this xpath for the correct attribute to change:
feature[property/@value='Artifact']/property[@name='driver']/@value
-->
<xsl:template match="feature[property/@value='Artifact']/property[@name='driver']/@value">
<xsl:attribute name="value">somedriver2</xsl:attribute>
</xsl:template>
<!--
A template match on with this xpath for the correct attribute to change:
feature[property/@value='Comments']/property[@name='driver']/@value
-->
<xsl:template match="feature[property/@value='Comments']/property[@name='driver']/@value">
<xsl:attribute name="value">somedriver3</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
XPath queries, it doesn't modify. As the name suggests, XPathSelectElements returns the nodes you want. You'll have to process each one of them and modify the correct child elements. The unconventional XML schema makes this harder than needed -Templates,Source, Comments etc should all be elements, not values in a generic attribute. No flexibility is gained by the current format.
LINQ is also a query, not a modification language, it doesn't modify. No matter what language you use you'll have to iterate over the feature elements they produce and for each one, retrieve the name or driver children and modify their value attribute.
Comment from Panagiotis Kanavos
Using this data
Dim docXE As XElement
'test data
docXE = <report>
<feature tag="Config"/>
<feature tag="Runtime">
<feature tag="Output">
<feature tag="Target">
<property name="type" value="Html"/>
<property name="driver" value="Telelogic.Html.Driver"/>
</feature>
<feature tag="Target">
<property name="type" value="Word"/>
<property name="driver" value="Telelogic.Word.Driver"/>
</feature>
<feature tag="Target">
<property name="type" value="PDF"/>
<property name="driver" value="Telelogic.Pdf.Driver"/>
</feature>
</feature>
<feature tag="Metadata">
<property name="date" value="16.01.2025"/>
<property name="time" value="09:31:34"/>
</feature>
<feature tag="Templates">
<feature tag="Template">
<property name="username" value="myself"/>
<property name="password" value="something"/>
<feature tag="Data sources">
<feature tag="Source">
<property name="name" value="modules"/>
<property name="driver" value="eval"/>
</feature>
<feature tag="Source">
<property name="name" value="Artifact"/>
<property name="driver" value="eval"/>
</feature>
<feature tag="Source">
<property name="name" value="Comments"/>
<property name="driver" value="eval"/>
</feature>
</feature>
</feature>
</feature>
</feature>
</report>
Try this code.
Dim selMod As List(Of XElement)
selMod = (From el In docXE...<feature>
Where el.@tag = "Source" AndAlso
el.<property>.@value = "modules"
From src In el.<property>
Where src.@name = "driver"
Select src).ToList
For Each el As XElement In selMod
el.@name = "FOODriver"
Next
XPathSelectElements
returns the nodes you want. You'll have to process each one of them and modify the correct child elements. The unconventional XML schema makes this harder than needed -Templates
,Source
,Comments
etc should all be elements, not values in a generic attribute. No flexibility is gained by the current format – Panagiotis Kanavos Commented Jan 16 at 11:55feature
elements they produce and for each one, retrieve thename
ordriver
children and modify theirvalue
attribute. – Panagiotis Kanavos Commented Jan 16 at 12:02