解析具有已知结构和重复元素的 XML 文件

2024-03-24

我正在尝试从包含大量具有重复名称的元素的 XML 文件中解析信息。

以下是我尝试解析的文件类型的示例,仅包含一条记录:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
    <!--
        Start of the FIRST record.
    -->
    <dict>
        <key>80211D_IE</key>
        <dict>
            <key>IE_KEY_80211D_CHAN_INFO_ARRAY</key>
            <array>
                <dict>
                    <key>IE_KEY_80211D_FIRST_CHANNEL</key>
                    <integer>1</integer>
                    <key>IE_KEY_80211D_MAX_POWER</key>
                    <integer>27</integer>
                    <key>IE_KEY_80211D_NUM_CHANNELS</key>
                    <integer>11</integer>
                </dict>
            </array>
            <key>IE_KEY_80211D_COUNTRY_CODE</key>
            <string>US</string>
        </dict>
        <key>AGE</key>
        <integer>0</integer>
        <key>AP_MODE</key>
        <integer>2</integer>
        <key>BEACON_INT</key>
        <integer>100</integer>
        <key>BSSID</key>
        <string>ac:5d:10:73:c3:11</string>
        <key>CAPABILITIES</key>
        <integer>1073</integer>
        <key>CHANNEL</key>
        <integer>2</integer>
        <key>CHANNEL_FLAGS</key>
        <integer>10</integer>
        <key>IE</key>
        <data>
        AAZPbGl2ZXIBCIKEiwwSlhgkAwECBwZVUyABCxswGAEAAA+sAgIAAA+sBAAP
        rAIBAAAPrAIAAN0aAFDyAQEAAFDyAgIAAFDyBABQ8gIBAABQ8gIqAQAyBDBI
        YGw=
        </data>
        <key>NOISE</key>
        <integer>0</integer>
        <key>RATES</key>
        <array>
            <integer>1</integer>
            <integer>2</integer>
            <integer>5</integer>
            <integer>6</integer>
            <integer>9</integer>
            <integer>11</integer>
            <integer>12</integer>
            <integer>18</integer>
            <integer>24</integer>
            <integer>36</integer>
            <integer>48</integer>
            <integer>54</integer>
        </array>
        <key>RSN_IE</key>
        <dict>
            <key>IE_KEY_RSN_AUTHSELS</key>
            <array>
                <integer>2</integer>
            </array>
            <key>IE_KEY_RSN_MCIPHER</key>
            <integer>2</integer>
            <key>IE_KEY_RSN_UCIPHERS</key>
            <array>
                <integer>4</integer>
                <integer>2</integer>
            </array>
            <key>IE_KEY_RSN_VERSION</key>
            <integer>1</integer>
        </dict>
        <key>RSSI</key>
        <integer>-74</integer>
        <key>SSID</key>
        <data>
        T2xpdmVy
        </data>
        <key>SSID_STR</key>
        <string>Oliver</string>
        <key>WPA_IE</key>
        <dict>
            <key>IE_KEY_WPA_AUTHSELS</key>
            <array>
                <integer>2</integer>
            </array>
            <key>IE_KEY_WPA_MCIPHER</key>
            <integer>2</integer>
            <key>IE_KEY_WPA_UCIPHERS</key>
            <array>
                <integer>4</integer>
                <integer>2</integer>
            </array>
            <key>IE_KEY_WPA_VERSION</key>
            <integer>1</integer>
        </dict>
    </dict>
    <!--
        End of the FIRST record.
        In reality, more records follow.
    -->
</array>
</plist>

我遇到的问题是,我想从本质上展平每个观察结果(上例中只有一个观察结果),以便第一个中的每个元素<array>(即每个<dict> within <array>) 是数据框中的一行,其中的每个元素<dict>是一列,由适当的名称命名<key>.

我已经尝试过以下功能XML包,主要是xmlToList,但还没有完全弄清楚解析 XML 数据的正确方法。


Edit:

我想要的输出或多或少是将每个记录展平为数据框或列表中的一行,以便可以通过键轻松访问值。我不一定关心保留任何层次结构,例如每条记录都会有<key>80211D_IE</key>随后是一个dict包含实际信息——<key>80211D_IE</key>不是必需的,因为它不包含任何真实信息,而只是一组项目的不必要的分组。我可以将其存储为列表,例如mydata$record1$X80211D_IE$I.E._KEY_80211D_CHAN_INFO_ARRAY$IE_KEY_80211D_FIRST_CHANNEL,或者在像这样的数据框中mydata[1, 'I.E._KEY_80211D_FIRST_CHANNEL'].

我现在遇到的最大问题是这个 XML 结构似乎不太适合解析。例如,如果我想将 XML 子集化为记录,其中SSID_STR匹配一个字符串,我不能只使用xmlToList因为它不知道键应该与其值相关联。所以我得到一个这样的列表:

> str(xmlToList("path/to/my/file.xml"), max.level=2)
List of 2
 $ array :List of 25
  ..$ dict:List of 36
  ..$ dict:List of 32
  ..$ dict:List of 32
  ..$ dict:List of 38
  ..$ dict:List of 36
  ..$ dict:List of 34
  ..$ dict:List of 34
  ..$ dict:List of 34
  ..$ dict:List of 34
  ..$ dict:List of 34
  ..$ dict:List of 32
  ..$ dict:List of 38
  ..$ dict:List of 38
  ..$ dict:List of 34
  ..$ dict:List of 36
  ..$ dict:List of 34
  ..$ dict:List of 36
  ..$ dict:List of 34
  ..$ dict:List of 36
  ..$ dict:List of 36
  ..$ dict:List of 40
  ..$ dict:List of 42
  ..$ dict:List of 36
  ..$ dict:List of 38
  ..$ dict:List of 38
 $ .attrs: Named chr "1.0"
  ..- attr(*, "names")= chr "version"

看看其中的一个

> str(xmlToList("path/to/my/file.xml")$array[[1]], max.level = 1)
List of 36
 $ key    : chr "80211D_IE"
 $ dict   :List of 4
 $ key    : chr "AGE"
 $ integer: chr "0"
 $ key    : chr "AP_MODE"
 $ integer: chr "2"
 $ key    : chr "BEACON_INT"
 $ integer: chr "100"
 $ key    : chr "BSSID"
 $ string : chr "a:18:a:31:0:83"
 $ key    : chr "CAPABILITIES"
 $ integer: chr "4145"
 $ key    : chr "CHANNEL"
 $ integer: chr "11"
 $ key    : chr "CHANNEL_FLAGS"
 $ integer: chr "10"
 $ key    : chr "HT_CAPS_IE"
 $ dict   :List of 12
 $ key    : chr "HT_IE"
 $ dict   :List of 34
 $ key    : chr "IE"
 $ data   : chr "\n\t\tAAR0ZXN0AQiWlgwSGCQwSAMBCwcGVVMgAQseKgEDMBgBAAAPrAICAAAPrAQA\n\t\tD6wCAQAAD6wCAAAyAmBsRgVzwAEAADMCDAstGowRG///AAAAAAAAAAA"| __truncated__
 $ key    : chr "NOISE"
 $ integer: chr "0"
 $ key    : chr "RATES"
 $ array  :List of 9
 $ key    : chr "RSN_IE"
 $ dict   :List of 8
 $ key    : chr "RSSI"
 $ integer: chr "-86"
 $ key    : chr "SSID"
 $ data   : chr "\n\t\tdGVzdA==\n\t\t"
 $ key    : chr "SSID_STR"
 $ string : chr "test"
 $ key    : chr "WPA_IE"
 $ dict   :List of 8

很容易看出,实际上只有 18 个项目,但密钥作为自己的项目存储(总共 36 个)。

The xmlToList函数实际上是almost我希望它做什么——而是使用相应键的值来命名包含数据的列表的元素。

这看起来像:

List of 18
 $ AGE          : chr "0"
 $ AP_MODE      : chr "2"
 $ BEACON_INT   : chr "100"
 $ BSSID        : chr "a:18:a:31:0:83"
 $ CAPABILITIES : chr "4145"
 $ CHANNEL      : chr "11"
 $ CHANNEL_FLAGS: chr "10"
 $ HT_CAPS_IE   :List of 12
 $ HT_IE        :List of 34
 $ IE           : chr "\n\t\tAAR0ZXN0AQiWlgwSGCQwSAMBCwcGVVMgAQseKgEDMBgBAAAPrAICAAAPrAQA\n\t\tD6wCAQAAD6wCAAAyAmBsRgVzwAEAADMCDAstGowRG///AAAAAAAAAAA"| __truncated__
 $ NOISE        : chr "0"
 $ RATES        :List of 9
 $ RSN_IE       :List of 8
 $ RSSI         : chr "-86"
 $ SSID         : chr "\n\t\tdGVzdA==\n\t\t"
 $ SSID_STR     : chr "test"
 $ WPA_IE       :List of 8
 $ X80211D_IE   :List of 4

在这个假设的输出中,使用适当的键很容易获取值。此外,继续取消嵌套列表(因为不需要分组结构)以生成数据框将很容易。


我将OP的XML存储在一个文件中但重复了所提供的单个记录!

使用一些附加的附加包这可能会更灵活(我会使用dplyr%>%),但我忍住了。我建议使用xml2代替XML。您可以使用 XPATH 表达式来定位感兴趣的节点。

x <- read_xml("so.xml")
(elements <- xml_find_all(x, ".//dict/dict/array/dict"))
#> {xml_nodeset (2)}
#> [1] <dict>\n                    <key>IE_KEY_80211D_FIRST_CHANNEL</key>\n ...
#> [2] <dict>\n                    <key>IE_KEY_80211D_FIRST_CHANNEL</key>\n ...

## isolate the key nodes ... will become variable names
keys <- lapply(elements, xml_find_all, "key")
keys <- lapply(keys, xml_text)
## I advise checking that keys are uniform across the records here!
(keys <- keys[[1]])
#> [1] "IE_KEY_80211D_FIRST_CHANNEL" "IE_KEY_80211D_MAX_POWER"    
#> [3] "IE_KEY_80211D_NUM_CHANNELS"

## isolate integer data
integers <- lapply(y, xml_find_all, "integer")
integers <- lapply(integers, xml_text)
integers <- lapply(integers, type.convert)
yay <- as.data.frame(do.call(rbind, integers))
names(yay) <- keys
yay
#>   IE_KEY_80211D_FIRST_CHANNEL IE_KEY_80211D_MAX_POWER
#> 1                           1                      27
#> 2                           1                      27
#>   IE_KEY_80211D_NUM_CHANNELS
#> 1                         11
#> 2                         11
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

解析具有已知结构和重复元素的 XML 文件 的相关文章

随机推荐