我有一组OpenStruct元素,我需要在Nokogiri的帮助下构建XML。
collection = [
OpenStruct.new(:catalogStoreNumber => '657758',
:catalogStoreId => 'CTH6536',
:catalogStoreLocation => 'UnitedStates',
:catalogOwnerId => 'TYCT11190',
:catalogOwner => 'McGrawHill Pub.',
:catalogList => OpenStruct.new(
:catalogProductInfo => OpenStruct.new(
:productType => 'Book',
:productName => 'The Client',
:productAuthorized => 'Y',
:productId => 'BKSUS113246A',
:productVerificationCode => '4546747',
:productPurcTransactionTime => '2012-05-21T13:36:38+05:30',
:productAuditDetails => OpenStruct.new(
:productAuditNo => '1',
:prodHandledByUser => 'StoreUserS14',
:productAuditTime => '2012-05-21T13:36:38+05:30',
:productAuditAdminId => 'McGr1132',
:productPurchaseRate => '50.14 Prcnt',
:productSystemLoggerId => 'UNX-NETW4536'
),
:productAuditDetails => OpenStruct.new(
:productAuditNo => '2',
:prodHandledByUser => 'OnlineUserOn008',
:productAuditTime => '2012-05-23T16:16:08+05:30',
:productAuditAdminId => 'McGr1132',
:productPurchaseRate => '84.86 Prcnt',
:productSystemLoggerId => 'UNX-NETW4536'
)
),
:catalogProductInfo => OpenStruct.new(
:productType => 'Pen',
:productName => 'Reynolds'
:productAuthorized => 'N',
:productId => 'PNSUS228886B',
:productVerificationCode => '2330076',
:productPurcTransactionTime => '2012-04-22T15:06:18+04:30',
:productAuditDetails => OpenStruct.new(
:productAuditNo => '1',
:prodHandledByUser => 'CCUserA14',
:productAuditTime => '2012-04-26T13:36:38+05:30',
:productAuditAdminId => 'ReyGr1132',
:productPurchaseRate => '20.19 Prcnt',
:productSystemLoggerId => 'WIN-NETW4536'
)
)
)
)]
我尝试使用以下代码..根据您的回答(精选元素)
builder = Nokogiri::XML::Builder.new do |xml|
xml.CatalogOrder do
collection.each do |ctlg|
xml.CatalogStoreNumber ctlg.catalogStoreNumber
xml CatalogStoreId ctlg.catalogStoreId
xml.CatalogOwnerId ctlg.catalogOwnerid
xml.CatalogOwner ctlg.catalogOwner
xml.CatalogList do
prod_count = 0
aud_list_count = 0
collection.each do |prod|
info = prod.catalogList[0].catalogProductInfo
xml.ProductInfo do
xml.ProductType info.productType
xml.ProductName info.productName
xml.ProductId info.productId
xml.ProductVerificationCode info.ProductVerificationCode
xml.ProductPurcTransactionTime info.productPurcTransactionTime
xml.ProductAuditDetails do
collection.each do |aud_dtl|
aud_info = aud_dtl.catalogList[0].catalogProductinfo[0].productAuditDetails
xml.ProductAuditNo aud_info.productAuditNo
xml.ProdHandledByUser aud_info.prodHandledByUser
xml.ProductAuditTime aud_info.productAuditTime
xml.ProductAuditAdminId aud_info.productAuditAdminId
xml.ProductPurchaseRate aud_info.productPurchaseRate
xml.ProductSystemLoggerId aud_info.productSystemLoggerId
# Do whatever you must above to concoct your ProductId
end
aud_list_count = aud_list_count + 1
end
prod_count = prod_count + 1
end
end
end
puts builder.to_xml
我需要输出如下......
<CatalogOrder>
<CatalogStoreNumber>657758</CatalogStoreNumber>
<CatalogStoreId>CTH6536</CatalogStoreId>
<CatalogStoreLocation>UnitedStates</CatalogStoreLocation>
<CatalogOwnerId>TYCT11190</CatalogOwnerId>
<CatalogOwner>McGrawHill Pub.</CatalogOwner>
<CatalogList>
<CatalogProductInfo>
<ProductType>Book</ProductType>
<ProductName>The Client</ProductName>
<ProductAuthorized>Y</ProductAuthorized>
<ProductId>BKSUS113246A</ProductId>
<ProductVerificationCode>4546747</ProductVerificationCode>
<ProductPurcTransactionTime>2012-05-21T13:36:38+05:30</ProductPurcTransactionTime>
<ProductAuditDetails>
<ProductAuditNo>1</ProductAuditNo>
<ProdHandledByUser>StoreUserS14</ProdHandledByUser>
<ProductAuditTime>2012-05-21T13:36:38+05:30</ProductAuditTime>
<ProductAuditAdminId>McGr1132</ProductAuditAdminId>
<ProductPurchaseRate>50.14 Prcnt</ProductPurchaseRate>
<ProductSystemLoggerId>WIN-NETW4536</ProductSystemLoggerId>
</ProductAuditDetails>
<ProductAuditDetails>
<ProductAuditNo>2</ProductAuditNo>
<ProdHandledByUser>OnlineUserOn008</ProdHandledByUser>
<ProductAuditTime>2012-05-23T16:16:08+05:30</ProductAuditTime>
<ProductAuditAdminId>McGr1132</ProductAuditAdminId>
<ProductPurchaseRate>84.86 Prcnt</ProductPurchaseRate>
<ProductSystemLoggerId>UNX-NETW4536</ProductSystemLoggerId>
</ProductAuditDetails>
</CatalogProductInfo>
<CatalogProductInfo>
<ProductType>Pen</ProductType>
<ProductName>Reynolds</ProductName>
<ProductAuthorized>N</ProductAuthorized>
<ProductId>PNSUS228886B</ProductId>
<ProductVerificationCode>2330076</ProductVerificationCode>
<ProductPurcTransactionTime>2012-04-22T15:06:18+04:30</ProductPurcTransactionTime>
<ProductAuditDetails>
<ProductAuditNo>1</ProductAuditNo>
<ProdHandledByUser>CCUserA14</ProdHandledByUser>
<ProductAuditTime>2012-04-26T13:36:38+05:30</ProductAuditTime>
<ProductAuditAdminId>ReyGr1132</ProductAuditAdminId>
<ProductPurchaseRate>20.19 Prcnt</ProductPurchaseRate>
<ProductSystemLoggerId>WIN-NETW4536</ProductSystemLoggerId>
</ProductAuditDetails>
</CatalogProductInfo>
</CatalogList>
</CatalogOrder>
我试图在嵌套的OpenStruct数组中循环,但无法找到合适的逻辑。
答案 0 :(得分:3)
require 'ostruct'
require 'nokogiri'
collection = [
OpenStruct.new(
:catalogStoreNumber => '657758',
:catalogStoreId => 'CTH6536',
:catalogStoreLocation => 'UnitedStates',
:catalogOwnerId => 'TYCT11190',
:catalogOwner => 'McGrawHill Pub.',
:catalogList => OpenStruct.new(
:catalogProductInfo => OpenStruct.new(
:productType => 'Book',
:productName => 'The Client'
)
)
)
]
builder = Nokogiri::XML::Builder.new do |xml|
xml.CatalogOrder do
xml.CatalogList do
collection.each do |prod|
info = prod.catalogList.catalogProductInfo
xml.ProductInfo do
xml.ProductType info.productType
xml.ProductName info.productName
xml.ProductId "#{prod.catalogOwnerId}-#{prod.catalogStoreNumber}"
# Do whatever you must above to concoct your ProductId
end
end
end
end
end
puts builder.to_xml
<?xml version="1.0"?>
<CatalogOrder>
<CatalogList>
<ProductInfo>
<ProductType>Book</ProductType>
<ProductName>The Client</ProductName>
<ProductId>TYCT11190-657758</ProductId>
</ProductInfo>
</CatalogList>
</CatalogOrder>
如果您想要更通用的转换(OpenStruct层次结构的XML表示),请参阅以下两种解决方案之一:
# Add all entries of an OpenStruct to an XML builder
# Recursively creates sub-nodes for OpenStruct instances
def ostruct_each(ostruct,xml)
ostruct.instance_variable_get(:@table).each do |field,value|
if value.is_a?(OpenStruct)
xml.send(field) do
ostruct_each(value,xml)
end
else
xml.send(field,value)
end
end
end
builder = Nokogiri::XML::Builder.new do |xml|
xml.CatalogOrder do
xml.CatalogList do
collection.each do |prod_info|
xml.ProductInfo do
ostruct_each(prod_info,xml)
end
end
end
end
end
puts builder.to_xml
<?xml version="1.0"?>
<CatalogOrder>
<CatalogList>
<ProductInfo>
<catalogStoreNumber>657758</catalogStoreNumber>
<catalogStoreId>CTH6536</catalogStoreId>
<catalogStoreLocation>UnitedStates</catalogStoreLocation>
<catalogOwnerId>TYCT11190</catalogOwnerId>
<catalogOwner>McGrawHill Pub.</catalogOwner>
<catalogList>
<catalogProductInfo>
<productType>Book</productType>
<productName>The Client</productName>
</catalogProductInfo>
</catalogList>
</ProductInfo>
</CatalogList>
</CatalogOrder>
# Create a NodeSet of elements for all attributes in an OpenStruct
# Recursively creates child elements for any value that is an OpenStruct
def ostruct_to_elements(xml_doc,ostruct)
Nokogiri::XML::NodeSet.new(
xml_doc,
ostruct.instance_variable_get(:@table).map do |name,val|
xml_doc.create_element(name.to_s).tap do |el|
el << (val.is_a?(OpenStruct) ? ostruct_to_elements(xml_doc,val) : val)
end
end
)
end
builder = Nokogiri::XML::Builder.new do |xml|
xml.CatalogOrder do
xml.CatalogList do
collection.each do |prod_info|
xml.ProductInfo do
xml.parent << ostruct_to_elements(xml.doc,prod_info)
end
end
end
end
end
puts builder.to_xml
答案 1 :(得分:0)
回答您更改的问题和数据。
这是您的源数据,总结如下:
collection = [
OpenStruct.new(
:foo => 'bar',
:list => OpenStruct.new(
:catalogProductInfo => OpenStruct.new(...)
:catalogProductInfo => OpenStruct.new(...)
)
)
]
我们注意到的第一件事是collection
是一个数组,但它只有一个项目。这看起来不太有用。
要注意的第二个更重要的事情是你试图将OpenStruct(内部的)用作数组。这完全失败了,因为第二个值完全覆盖了第一个:
require 'ostruct'
p OpenStruct.new( a:1, a:2 )
#=> #<OpenStruct a=2>
结构中的同一个键不能有两个值。相反,您可能想要在对象内部使用数组值。例如:
root = OpenStruct.new(
:foo => 'bar',
:list => [ # this is an array of two distinct objects
OpenStruct.new( :catalogProductInfo => OpenStruct.new(...) ),
OpenStruct.new( :catalogProductInfo => OpenStruct.new(...) )
]
)
第三,正如我前面提到的,我认为你没有充分的理由在这里使用OpenStruct。相反,只需使用哈希文字。结合我在其他答案中提供的代码,这就是您的数据(简化)和工作解决方案的样子:
MyOrder = {
catalogStoreNumber: '657758', # Ruby 1.9 Hash syntax;
catalogStoreId: 'CTH6536', # same as :catalogStoreId => 'CTH536'
catalogList: {
catalogProductInfo: [
{
productType: 'Book',
productName: 'The Client',
productAuditDetails: [
{ productAuditNo: '1', prodHandledByUser: 'StoreUserS14' },
{ productAuditNo: '2', prodHandledByUser: 'OnlineUserOn008' }
]
},
{
productType: 'Pen',
productName: 'Reynolds',
productAuditDetails: [
{ productAuditNo: '1', prodHandledByUser: 'CCUserA14' }
]
}
]
}
}
# Adds key/value pairs from a Hash to a Nokogiri::XML::Builder
def hash2xml(hash,xml)
hash.each do |field,value|
name = field.to_s.sub(/^./,&:upcase) # convert "fooBar" to "FooBar"
case value
when Hash then xml.send(name){ hash2xml(value,xml) }
when Array then value.each{ |o| xml.send(name){ hash2xml(o,xml) } }
else xml.send(name,value)
end
end
end
builder = Nokogiri::XML::Builder.new do |xml|
xml.CatalogOrder do
hash2xml(MyOrder,xml)
end
end
puts builder.to_xml
<?xml version="1.0"?>
<CatalogOrder>
<CatalogStoreNumber>657758</CatalogStoreNumber>
<CatalogStoreId>CTH6536</CatalogStoreId>
<CatalogList>
<CatalogProductInfo>
<ProductType>Book</ProductType>
<ProductName>The Client</ProductName>
<ProductAuditDetails>
<ProductAuditNo>1</ProductAuditNo>
<ProdHandledByUser>StoreUserS14</ProdHandledByUser>
</ProductAuditDetails>
<ProductAuditDetails>
<ProductAuditNo>2</ProductAuditNo>
<ProdHandledByUser>OnlineUserOn008</ProdHandledByUser>
</ProductAuditDetails>
</CatalogProductInfo>
<CatalogProductInfo>
<ProductType>Pen</ProductType>
<ProductName>Reynolds</ProductName>
<ProductAuditDetails>
<ProductAuditNo>1</ProductAuditNo>
<ProdHandledByUser>CCUserA14</ProdHandledByUser>
</ProductAuditDetails>
</CatalogProductInfo>
</CatalogList>
</CatalogOrder>