.netxmlnamespaceslinq-to-xml

.net reading xml element values when name has colon


I'm using VB.Net and want to read specific values from an xml file (it's an xmp file from DarkTable). I'm wanting to read the notes and tag values (when they exist) indicated below as '*** THIS VALUE ***'.

I can usually fumble my way through reading xml values but the colons and namespaces are an extra challenge.

Any help is appreciated. Thank you.

What I have so far is:

    Private Function GetHTMLForTagsAndNotes(ByVal ImageFullPath As String) As String

        Dim strXMPFilename As String = ImageFullPath & ".xmp"
        Dim doc As New Xml.XmlDocument()
        Dim strNote As String
        Dim lstTags As New List(Of String)
        Dim strReturnHTML As String = ""

        doc.Load(strXMPFilename)

        Try
            'Read xml notes and tags 
            '(START: NEED HELP HERE PLEASE)

            'If an acdsee:notes value exists
            strNote = "*** THIS VALUE ***"

            'For each tag that exists
            lstTags.Add("*** THIS VALUE ***")

            '(END: NEED HELP HERE PLEASE)

        Catch ex As Exception
            MsgBox(ex.Message)
        End Try

        'Format the tags and/or note as HTML and return

        Return strReturnHTML

    End Function

The XML format is:

<?xml version="1.0" encoding="UTF-8"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 4.4.0-Exiv2">
 <rdf:RDF xmlns:rdf="URL//www.w3.org/1999/02/22-rdf-syntax-ns#">
  <rdf:Description rdf:about=""
    xmlns:exif="URL//ns.adobe.com/exif/1.0/"
    xmlns:xmp="URL//ns.adobe.com/xap/1.0/"
    xmlns:xmpMM="URL//ns.adobe.com/xap/1.0/mm/"
    xmlns:darktable="URL//darktable.sf.net/"
    xmlns:acdsee="URL//ns.acdsee.com/iptc/1.0/"
    xmlns:dc="URL//purl.org/dc/elements/1.1/"
    xmlns:lr="URL//ns.adobe.com/lightroom/1.0/"
   exif:DateTimeOriginal=""
   xmp:Rating="1"
   xmpMM:DerivedFrom="SCAN_20230401_0392.jpg"
   darktable:import_timestamp="63815945870187001"
   darktable:change_timestamp="-1"
   darktable:export_timestamp="63841000148835795"
   darktable:print_timestamp="-1"
   darktable:xmp_version="5"
   darktable:raw_params="0"
   darktable:auto_presets_applied="1"
   darktable:history_end="4"
   darktable:iop_order_version="2"
   darktable:history_basic_hash="c80077d119ae746632f44df3493dfd64"
   darktable:history_current_hash="c80077d119ae746632f44df3493dfd64"
   acdsee:notes="*** THIS VALUE ***">
   <darktable:masks_history>
    <rdf:Seq/>
   </darktable:masks_history>
   <darktable:history>
    <rdf:Seq>
     <rdf:li
      darktable:num="0"
      darktable:operation="colorin"
      darktable:enabled="1"
      darktable:modversion="7"
      darktable:params="gz48eJxjZBgFowABWAbaAaNgwAEAEDgABg=="
      darktable:multi_name=""
      darktable:multi_priority="0"
      darktable:blendop_version="11"
      darktable:blendop_params="gz14eJxjYIAACQYYOOHEgAZY0QVwggZ7CB6pfNoAAE8gGQg="/>
    </rdf:Seq>
   </darktable:history>
   <dc:subject>
    <rdf:Bag>
     <rdf:li>*** THIS VALUE ***</rdf:li>
     <rdf:li>*** THIS VALUE ***</rdf:li>
     <rdf:li>*** THIS VALUE ***</rdf:li>
     <rdf:li>*** THIS VALUE ***</rdf:li>
    </rdf:Bag>
   </dc:subject>
   <lr:hierarchicalSubject>
    <rdf:Bag>
     <rdf:li>*** THIS VALUE ***</rdf:li>
     <rdf:li>*** THIS VALUE ***</rdf:li>
     <rdf:li>*** THIS VALUE ***</rdf:li>
     <rdf:li>*** THIS VALUE ***</rdf:li>
    </rdf:Bag>
   </lr:hierarchicalSubject>
  </rdf:Description>
 </rdf:RDF>
</x:xmpmeta>

Solution

  • Please try the following solution based on LINQ to XML API. It is available in the .Net Framework since 2007.

    Notable points:

    1. You need to declare a default namespace, because all XML elements in question are bound to that default namespace.
    2. We are looking for all <rdf:li> XML elements that have <rdf:Bag> XML element as a parent.

    VB.NET

    Sub Main
        Const FILENAME As String = "e:\Temp\JakeSmith.xml"
        
        Dim xdoc As XDocument = XDocument.Load(FILENAME)
        Dim ns As XNamespace = "URL//www.w3.org/1999/02/22-rdf-syntax-ns#"
        Dim ns1 As XNamespace = "URL//ns.acdsee.com/iptc/1.0/"
        
        Dim listOfString As List(Of String) = xdoc.Descendants(ns + "li") _
            .Where(Function(x) x.Parent.Name.LocalName = "Bag") _
            .[Select](Function(x) x.Value).ToList()
        Console.Write(listOfString)
        
        Dim notes As String = xdoc.Descendants(ns + "Description") _
            .Attributes(ns1 + "notes").FirstOrDefault()?.Value
        Console.Write(notes)
    End Sub
    

    Output

    List (8 items)
    *** THIS VALUE ***
    *** THIS VALUE ***
    *** THIS VALUE ***
    *** THIS VALUE ***
    *** THIS VALUE ***
    *** THIS VALUE ***
    *** THIS VALUE ***
    *** THIS VALUE ***