使用 hive 解析 XML 中的多次出现问题

Posted

技术标签:

【中文标题】使用 hive 解析 XML 中的多次出现问题【英文标题】:Multiple occurance issue in XML Parsing using hive 【发布时间】:2016-05-25 14:31:36 【问题描述】:

我正在尝试使用 com.ibm.spss.hive.serde2.xml.XmlSerDe 从 XML 文件中创建 Hive 表。这对于单次出现的标签非常有效。但我有一个多次出现的问题。

下面是我的源 XML。

<Item>
      <TimeStamp>2016-02-19T12:27:06.387Z</TimeStamp>
      <AlsoSeen End="2014-08-21T13:44:32.557Z" Start="2014-08-21T13:44:04.637Z" />
      <AlsoSeen End="2014-08-21T13:44:33.557Z" Start="2014-08-21T13:45:04.637Z" />
      <AlsoSeen End="2014-08-21T13:44:34.557Z" Start="2014-08-21T13:46:04.637Z" />
      <Title ID="112031424">FAULT IN OUR STARS, THE</Title>
      <FileName>The Fault in Our Stars (2014) EXTENDED HDRip x264 AAC-CPG</FileName>
</Item>

下面是我的 Hive Table DDL

add jar hivexmlserde-1.0.5.3.jar;

CREATE EXTERNAL TABLE xml_test
(
         Item_TimeStamp String
         ,Item_AS_Start  String
         ,Item_AS_End    String
         ,Item_Title     String
         ,Item_ID        String
         ,Item_Artist    String
         ,Item_Author    String
         ,Item_FileName  String
)
ROW FORMAT SERDE 'com.ibm.spss.hive.serde2.xml.XmlSerDe'
WITH SERDEPROPERTIES (
"column.xpath.Item_TimeStamp"="/Item/TimeStamp/text()",
"column.xpath.Item_AS_Start"="/Item/AlsoSeen/@Start",
"column.xpath.Item_AS_End"="/Item/AlsoSeen/@End",  
"column.xpath.Item_Title"="/Item/Title/text()",   
"column.xpath.Item_ID"="/Item/Title/@ID",      
"column.xpath.Item_FileName"="/Item/FileName/text()"
)
STORED AS
INPUTFORMAT 'com.ibm.spss.hive.serde2.xml.XmlInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.IgnoreKeyTextOutputFormat'
LOCATION '/user/xxxxxx/XML_text'
TBLPROPERTIES (
"xmlinput.start"="<Item>",
"xmlinput.end"="</Item>"
);

当我查询表格时,我得到以下值

2016-02-19T12:27:06.387Z <string>2014-08-21T13:44:04.637Z2014-08-21T13:45:04.637Z2014-08-21T13:46:04.637Z</string> <string>2014-08-21T13:44:32.557Z2014-08-21T13:44:33.557Z2014-08-21T13:44:34.557Z</string> FAULT IN OUR STARS, THE 112031424 The Fault in Our Stars (2014) EXTENDED HDRip x264 AAC-CPG 

但我预计输出应该是 3 个不同的行,用于 3 个不同出现的 AlsoSeen 标签,如下所示

2016-02-19T12:27:06.387Z  2014-08-21T13:44:04.637Z 2014-08-21T13:44:32.557Z FAULT IN OUR STARS, THE 112031424 The Fault in Our Stars (2014) EXTENDED HDRip x264 AAC-CPG
2016-02-19T12:27:06.387Z  2014-08-21T13:45:04.637Z 2014-08-21T13:44:33.557Z FAULT IN OUR STARS, THE 112031424 The Fault in Our Stars (2014) EXTENDED HDRip x264 AAC-CPG  
2016-02-19T12:27:06.387Z  2014-08-21T13:46:04.637Z 2014-08-21T13:44:34.557Z FAULT IN OUR STARS, THE 112031424 The Fault in Our Stars (2014) EXTENDED HDRip x264 AAC-CPG

谁能帮帮我?

【问题讨论】:

我对 SerDe 一无所知,但表达式确实看起来像 XPath;所以/Item/AlsoSeen[2]/@Start 会给你在每个元素&lt;Item&gt; 内第二次出现元素&lt;AlsoSeen&gt; 的属性。但是 AFAIK 你不能用 XPath 迭代,你需要 Java 代码——或者一个 XSLT 样式表。 顺便说一句,从结果来看,我猜 SerDe 会为所有 XPath 表达式找到“最细粒度的公共元素”,假设它意味着 一条记录,然后从拆分源开始在将每个 XPath 表达式输入列(即单元格)之前,“记录”中的 XML。 所以我建议您在将 XML 加载到 Hadoop 之前对其进行转换。使用 Linux 实用程序xsltproc 和一个简单的 XSLT 脚本,您可以获得一个包含 1 列的 CSV,其中所有“开始”属性由空格 分隔(对于自定义分隔符,您需要一个符合 XSLT 2 的解析器,即 Saxon ) -- 然后你可以在 Hive 中读取 CSV 并将数组“分解”成多行。多个数组更棘手,但可以通过“索引爆炸”来完成,并通过仅选择具有匹配索引的开始和结束值来过滤掉笛卡尔混乱。 @SamsonScharfrichter 你说得对,它是 Xpath。 Xpath 支持像 *@*node() 这样的通配符,它​​们将匹配多个值。 SerDe 是 HIVE 的 Java 库约定,用于对数据进行序列化和反序列化,并且有许多文件格式的 SerDe。 Hive 可以使用 'delimited by' 或 SerDe 来定义文件格式。在这种情况下,XML SerDe 将进行迭代。源代码在这里:github.com/dvasilen/Hive-XML-SerDe。我很惊讶这个例子有效,请参阅下面的答案。 【参考方案1】:

文档对此有一些示例。 https://github.com/dvasilen/Hive-XML-SerDe/wiki/XML-data-sources

使用text()@Elementname 为您提供单个(原始)值,并且您的表已被声明为所有STRING 列,您可能需要复杂类型来保存多个值,即Map、Struct 或Array .

您看到的行为与上述链接中的文档中的(5. 复杂类型)有关。

"被用作原始类型的复杂内容将被转换为 通过添加名为&lt;string&gt;"的根元素来获得有效的 XML 字符串

在您的示例中,您看到的结果是 @Start 属性的 3 个值连接起来,然后用 &lt;string&gt; 标记包装:

<string>2014-08-21T13:44:04.637Z2014-08-21T13:45:04.637Z2014-08-21T13:46:04.637Z</string>

您的 XPath 使用的是 @Start@End,而不是 /* 模式,所以我很惊讶它完全有效。

我了解您想要的结果,但我不确定它是否真的与数据建模方式相匹配。我认为&lt;Item&gt; 实际上是单行。 Item 有一个&lt;TimeStamp&gt; 和一个&lt;Title&gt;,它有一个@ID 属性,它还有一个名为&lt;AlsoSeen&gt; 的属性,它可以有多个@End 和@Start 值。从 XML 到 Hive 表的直接映射可能是这种复杂的数据类型: ARRAY&lt;STRUCT&lt;End: TIMESTAMP, Start: TIMESTAMP&gt;&gt; ,包含结束和开始时间戳的结构数组。

很遗憾,您的数据不符合 Hive 期望的时间戳字符串格式,因此您可以使用 STRING,如 https://cwiki.apache.org/confluence/display/Hive/LanguageManual+Types#LanguageManualTypes-timestamp

所以你最终会得到这个表创建语句(我已经删除了原始创建表中的 Item_Artist 和 Item_Author,因为你在 SERDEPROPERTIES 中没有与这些列匹配的 xpath)

CREATE EXTERNAL TABLE xml_test
(
         Item_TimeStamp String
         ,Item_AlsoSeen ARRAY<STRUCT<End: STRING, Start: STRING>>
         ,Item_Title     String
         ,Item_ID        String
         ,Item_FileName  String
)
ROW FORMAT SERDE 'com.ibm.spss.hive.serde2.xml.XmlSerDe'
WITH SERDEPROPERTIES (
"column.xpath.Item_TimeStamp"="/Item/TimeStamp/text()",
"column.xpath.Item_AlsoSeen"="/Item/AlsoSeen", 
"column.xpath.Item_Title"="/Item/Title/text()",   
"column.xpath.Item_ID"="/Item/Title/@ID",      
"column.xpath.Item_FileName"="/Item/FileName/text()"
)
STORED AS
INPUTFORMAT 'com.ibm.spss.hive.serde2.xml.XmlInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.IgnoreKeyTextOutputFormat'
LOCATION '/user/xxxxxx/XML_text'
TBLPROPERTIES (
"xmlinput.start"="<Item>",
"xmlinput.end"="</Item>"
);

注意我用于 AlsoSeen 字段的 xpath:"/Item/AlsoSeen"

这是上面链接的文档中的数组和结构的模式。

它会给你类似的结果

+--------------------+--------------------+--------------------+---------+--------------------+
|      Item_TimeStamp|       Item_AlsoSeen|          Item_Title|  Item_ID|       Item_FileName|
+--------------------+--------------------+--------------------+---------+--------------------+
|2016-02-19T12:27:...|[[2014-08-21T13:4...|FAULT IN OUR STAR...|112031424|The Fault in Our ...|
+--------------------+--------------------+--------------------+---------+--------------------+

Item_AlsoSeen 列包含这个,一个由逗号分隔的 Structs [] 的 Array(),每个结构都有结束和开始。

WrappedArray([2014-08-21T13:44:32.557Z,2014-08-21T13:44:04.637Z], [2014-08-21T13:44:33.557Z,2014-08-21T13:45:04.637Z], [2014-08-21T13:44:34.557Z,2014-08-21T13:46:04.637Z])

从那里,您可以使用LATERAL VIEW explode() 技术查询您想要的结果集

例如

SELECT
Item_TimeStamp,
    --Item_AlsoSeen,
    ias.End,
    ias.Start,
    Item_Title, 
    Item_ID, 
    Item_FileName
FROM xml_test
    LATERAL VIEW explode(Item_AlsoSeen) ias as ias

请注意,由于我将 AlsoSeen 创建为结构数组,explode() 函数将每个结构作为一行返回。 LATERAL VIEW 然后有效地进行交叉连接或可能更好地称为CROSS APPLY 以获得笛卡尔积。

+--------------------+--------------------+--------------------+--------------------+---------+--------------------+
|      Item_TimeStamp|                 End|               Start|          Item_Title|  Item_ID|       Item_FileName|
+--------------------+--------------------+--------------------+--------------------+---------+--------------------+
|2016-02-19T12:27:...|2014-08-21T13:44:...|2014-08-21T13:44:...|FAULT IN OUR STAR...|112031424|The Fault in Our ...|
|2016-02-19T12:27:...|2014-08-21T13:44:...|2014-08-21T13:45:...|FAULT IN OUR STAR...|112031424|The Fault in Our ...|
|2016-02-19T12:27:...|2014-08-21T13:44:...|2014-08-21T13:46:...|FAULT IN OUR STAR...|112031424|The Fault in Our ...|
+--------------------+--------------------+--------------------+--------------------+---------+--------------------+

参考 Hive 数据类型:https://cwiki.apache.org/confluence/display/Hive/LanguageManual+Types

【讨论】:

【参考方案2】:

有自定义编写的 UDF,它们是 array_index , numeric_range 很容易解决问题。为了使用这些函数,列类型应该是一个数组。请参考帖子Hive Explode / Lateral View multiple arrays

【讨论】:

以上是关于使用 hive 解析 XML 中的多次出现问题的主要内容,如果未能解决你的问题,请参考以下文章

HIVE优化

Hive性能优化

使用 JAXB 解组多次出现的 XML 元素

计算 json 对象中键的出现次数 - IMPALA/HIVE

XML - 使用 XSLT 合并同一节点多次出现的子元素

iPhone解析XML数据,标签未出现