I have an XML file that I want to search for the value of "val" using the Contains command and get its "index" attribute as well as the "catalog name" attribute.
<list>
<catalog index="1" name="n1">
<val index="1">sample text 1</val>
<val index="2">sample text 2</val>
<val index="3">sample text 3</val>
</catalog>
<catalog index="2" name="n2">
<val index="1">sample text 0</val>
<val index="2">sample text 2</val>
<val index="3">sample text 3</val>
<val index="4">sample text 1</val>
<val index="5">sample text 5</val>
<val index="6">sample text 6</val>
</catalog>
<catalog index="3" name="n3">
<val index="1">sample text 8</val>
<val index="2">sample text 9</val>
<val index="3">sample text 10</val>
</catalog>
</list>
I used
Dim xml_Doc = XDocument.Load(myPath & "list.Xml")
Dim search_result As IEnumerable(Of XElement)
search_result =
(From c In xml_Doc.Descendants("catalog")
Where c.Elements("val").Value.Contains("sample text 1")
Select c.Elements("val").Attributes("index").ToString & c.Attribute("name").Value)
How can I do that?
The output should be as follows:
index:1 , name: n1
index:4 , name: n2
CodePudding user response:
The easiest way to do this looks to be a nested From
query over the c.Elements("val")
elements like so:
Dim sampleText = "sample text 1"
Dim search_result =
(From c In xml_Doc.Descendants("catalog")
From v in c.Elements("val")
Where v.Value.Contains(sampleText)
Select New With {.index = v.Attribute("index").Value, .name = c.Attribute("name").Value })
For Each s In search_result
Console.WriteLine("index:{0} , name:{1}", s.index, s.name)
Next
Which produces:
index:1 , name:n1
index:4 , name:n2
index:3 , name:n3
Notes:
Doing it this way allows you to easily filter on the value of the inner element
v
but then select attributes from bothv
and the outer elementc
.index:3 , name:n3
is included in the results because the text of this element,sample text 10
, contains the search stringsample text 1
. If you do not want this element included, change yourWhere
clause to use equality:Dim search_result = (From c In xml_Doc.Descendants("catalog") From v in c.Elements("val") Where v.Value = sampleText Select New With {.index = v.Attribute("index").Value, .name = c.Attribute("name").Value })
CodePudding user response:
Disclaimer: I love XmlSerialization. Strong typing and reusable objects. I would tackle this problem first by creating some classes to serialize the file to
Imports System.IO
Imports System.Xml.Serialization
<XmlRoot("list")>
Public Class List
<XmlElement("catalog")>
Public Property Catalogs As List(Of Catalog)
End Class
Public Class Catalog
<XmlElement("val")>
Public Property Vals As List(Of Val)
<XmlAttribute("index")>
Public Property Index As Integer
<XmlAttribute("name")>
Public Property Name As String
End Class
Public Class Val
<XmlAttribute("index")>
Public Property Index As Integer
<XmlText>
Public Property Text As String
End Class
Then deserialize the file
Dim list As List
Dim serializer As New XmlSerializer(GetType(List))
Using sr As New StreamReader("list.xml")
list = CType(serializer.Deserialize(sr), List)
End Using
Now your xml is in .NET classes and you can use LINQ to get what you're after
Dim searchString = "sample text 1"
Dim catalogs As New List(Of Catalog)()
For Each catalog In list.Catalogs
Dim vals = catalog.Vals.Where(Function(val) val.Text = searchString)
For Each val In vals
catalogs.Add(New Catalog() With {.Vals = {val}.ToList(), .Name = catalog.Name, .Index = catalog.Index})
Next
Next
Dim search_results = catalogs.SelectMany(Function(c) c.Vals.Select(Function(v) $"index:{v.Index}, name: {c.Name}"))
For Each search_result In search_results
Console.WriteLine(search_result)
Next
Output:
index:1, name: n1
index:4, name: n2
It gets a little unwieldy because you want m items out of an n to m relationship (n catalogs and m vals) so we use SelectMany to project catalogs into the number of vals. But the object list
has all your data you can use to other ends either way.
As noted in the other answer, you were searching for strings containing "sample text 1" but that returns "sample text 10", and according to your desired output, you should search for an exact match.