How to parse XML in VBA and retrieve specific values

前端 未结 3 787
慢半拍i
慢半拍i 2021-01-15 01:50

I\'ve already spent two weeks searching unsuccessfully how to parse one specific XML and fetch just few values. I already tried every single code on internet until I found o

相关标签:
3条回答
  • 2021-01-15 02:33

    I've reached this solution, mixing the two answers, and sharing the code to help others.

    First I set the property and then used the iteration to retrieve the values I needed, I don't know if this is the best solution, since I can't control the XML structure and if they change their file I'll need to return to this code.

    I tried to work in a "Safety Line" to avoid any mistake in the output, but no problem for me to double check since I have access to the data itself.

    If node.childNodes(0).text = "Production" And node.childNodes(6).text = "Argentina" Then
    

    To ensure that the name and response will bring whatever I want.

    Public Sub test_3()
        Dim xmlDoc As MSXML2.DOMDocument60
        Dim URL As String, APIkey As String
    
        APIkey = "8DB688F8-1E22-4031-B581-59C221ECDDA6"
    
        URL = "https://apps.fas.usda.gov/PSDOnlineDataServices/api/CommodityData/GetCommodityDataByYear?commodityCode=2222000&marketYear=2018"
    
        Set xmlDoc = New MSXML2.DOMDocument60
    
        With CreateObject("MSXML2.XMLHTTP")
            .Open "GET", URL, False
            .SetRequestHeader "Accept", "text/xml"
            .SetRequestHeader "API_KEY", APIkey
            .Send
            xmlDoc.loadXML .ResponseText
            xmlDoc.SetProperty "SelectionNamespaces", "xmlns:r='http://schemas.datacontract.org/2004/07/PSDOnline.DataServices.Models'"
        End With
    
        Dim node As IXMLDOMElement, r As Long
    
        For Each node In xmlDoc.selectNodes("//r:CommodityData")
            If node.childNodes(0).text = "Production" And node.childNodes(6).text = "Argentina" Then
            r = r + 1
            Debug.Print node.childNodes(0).text
            Debug.Print node.childNodes(6).text
            Debug.Print node.LastChild.text
            'With ActiveSheet
                '.Cells(r, 1) = node.childNodes(0).text
                '.Cells(r, 2) = node.childNodes(6).text
                '.Cells(r, 3) = node.LastChild.text
            'End With
            End If
        Next
    End Sub
    

    This solution return the following response in the DEBUGGER:

    Production

    Argentina

    55300.0000

    Exactly what I wanted.

    Thanks again for the time and for sharing knowledge.

    0 讨论(0)
  • 2021-01-15 02:51

    It's a namespace issue I think. There are people more familiar with this who can likely fix how to add properly and then reference. I did try adding the two namespaces with the usual syntax .setProperty "SelectionNamespaces", namespace but still failed to set objects so guess I did something wrong.

    An interim, less robust solution is as follows:

    Option Explicit
    Public Sub test()
        Dim xmlDoc As MSXML2.DOMDocument60
        Dim URL As String, APIkey As String
    
        APIkey = "key"
    
        URL = "https://apps.fas.usda.gov/PSDOnlineDataServices/api/CommodityData/GetCommodityDataByYear?commodityCode=2222000&marketYear=2018"
    
        Set xmlDoc = New MSXML2.DOMDocument60
    
        With CreateObject("MSXML2.XMLHTTP")
            .Open "GET", URL, False
            .SetRequestHeader "Accept", "text/xml"
            .SetRequestHeader "API_KEY", APIkey
            .Send
            xmlDoc.LoadXML .responseText
        End With
    
        Dim node As IXMLDOMElement, r As Long
        For Each node In xmlDoc.SelectNodes("/*[name()='ArrayOfCommodityData']/*[name()='CommodityData']")
            r = r + 1
            With ActiveSheet
                .Cells(r, 1) = node.ChildNodes(0).Text
                .Cells(r, 2) = node.ChildNodes(6).Text
                .Cells(r, 3) = node.ChildNodes(11).Text
            End With
        Next
    End Sub
    
    0 讨论(0)
  • 2021-01-15 02:52

    There are two separate issues here.

    MSXML2 has issues using XPath when the XML document has a default namespace - see here for details. At the start of the downloaded document from the USDA site, there are some namespace declarations:

    <ArrayOfCommodityData xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/PSDOnline.DataServices.Models">
    

    There are two namespaces declared here. One with the prefix i and a default namespace that covers any element which does not have a namespace prefix. If you look at a "CalendarYear" entry in the XML document - <CalendarYear i:nil="true" /> - then you can see that "CalendarYear" is in the default namespace whereas "nil" is in the "i" namespace.

    To make MSXML2 work with default namespaces, you have to declare a namespace which has the same URI as the default namespace. This is done using the SelectionNamespaces property of the XML document, like this:

    xmlDoc.SetProperty "SelectionNamespaces", "xmlns:r='http://schemas.datacontract.org/2004/07/PSDOnline.DataServices.Models'"
    

    I chose r as the namespace but the name you choose is irrelevant - it just has to be different from any other namespaces in the document.

    This leads on to the second problem. You are using getElementsByTagName which just takes a tag name as a parameter but you are passing in an XPath string. To deal with an XPath string, you need to use SelectNodes instead and you need to use the namespace we added, like this:

    Set xmlNodeList = xmlDoc.SelectNodes("//r:AttributeDescription")
    
    0 讨论(0)
提交回复
热议问题