MaReBo Ответов: 1

Загрузка XML-содержимого в набор данных


Привет,
нужна небольшая помощь для создания dataset-объекта из XML-файла. Содержимое файла-это ответ на запрос на сервере, поэтому У меня нет никакого контроля над XML-структурой. Содержимое файла (filename "TestProd5.xml") есть:

<?xml version="1.0"?>
<ProductionOrder xmlns="http://www.sap.com/SBO/DIS">
	<AbsoluteEntry>17830</AbsoluteEntry>

		<ProductionOrderLine>
			<LineNumber>0</LineNumber>
			<BatchNumbers/>
		</ProductionOrderLine>

		<ProductionOrderLine>
			<LineNumber>1</LineNumber>
			<BatchNumbers>
				<BatchNumber>
					<BatchNumber>234</BatchNumber>
					<Quantity>1.000000</Quantity>
					<BaseLineNumber>1</BaseLineNumber>
				</BatchNumber>
			</BatchNumbers>
		</ProductionOrderLine>

		<ProductionOrderLine>
			<LineNumber>2</LineNumber>
			<BatchNumbers>
				<BatchNumber>
					<BatchNumber>234</BatchNumber>
					<Quantity>2.000000</Quantity>
					<BaseLineNumber>2</BaseLineNumber>
				</BatchNumber>
			</BatchNumbers>
		</ProductionOrderLine>

		<ProductionOrderLine>
			<LineNumber>3</LineNumber>
			<BatchNumbers/>
		</ProductionOrderLine>
</ProductionOrder>


Что я уже пробовал:

Я пытаюсь загрузить данные с помощью:

Dim dsProd As DataSet = New DataSet()
dsProd.ReadXml("TestProd5.xml")


Работает довольно хорошо, за исключением элементов <BatchNumber>234</BatchNumber> оммит в наборе данных. Как я предполагаю, причина-это родительский элемент с тем же именем. Мне нужно это значение в
объект DataSet. Что я могу сделать?

Любая помощь приветствуется.


Манфред

1 Ответов

Рейтинг:
4

Maciej Los

Существует несколько способов загрузки XML-данных, используя:

1) XmlDocument[^]
2) XDocument[^]
3) XmlSerialization[^] => Примеры сериализации XML | Microsoft Docs[^]

[РЕДАКТИРОВАТЬ]
Как я упоминал ранее, вы можете использовать класса XDocument используется для чтения данных в DataSet объект. Это идея:

Function LoadDataIntoDataSet() As DataSet

	Dim xNamespace As XNamespace = "http://www.sap.com/SBO/DIS"
	Dim xDoc As XDocument = XDocument.Parse(GetXmlData())
	Dim ds As DataSet = New DataSet()
	Dim pl As DataTable = New DataTable()
	pl.Columns.Add(New DataColumn("LineNumber", Type.GetType("System.Int32")))
	
	Dim bn As DataTable = New DataTable()
	bn.Columns.Add(New DataColumn("LineNumber", Type.GetType("System.Int32")))
	bn.Columns.Add(New DataColumn("BatchNumber", Type.GetType("System.Int32")))
	bn.Columns.Add(New DataColumn("Quantity", Type.GetType("System.String")))
	bn.Columns.Add(New DataColumn("BaseLineNumber", Type.GetType("System.Int32")))
	
	For Each pol As XElement In xDoc.Descendants(xNamespace + "ProductionOrderLine")
		pl.Rows.Add(New Object(){pol.Element(xNamespace + "LineNumber").Value})
		For Each ban As XElement In pol.Descendants(xNamespace + "BatchNumber").Where(Function(x) x.Descendants.Count>0) 
			bn.Rows.Add(New Object() _
				{ _
					pol.Element(xNamespace + "LineNumber").Value, _
					ban.Element(xNamespace + "BatchNumber").Value, _
					ban.Element(xNamespace + "Quantity").Value, _
					ban.Element(xNamespace + "BaseLineNumber").Value _
				})
		Next
	Next
	
	ds.Tables.Add(pl)
	ds.Tables.Add(bn)
	Return ds

End Function


Но я бы настоятельно рекомендовал использовать сериализацию (через Класс DataContractSerializer (System.Runtime.Сериализация) | Microsoft Docs[^]). В этом случае вам понадобится набор определений классов:

'needs fererence to System.Runtime.Serialization.dll
<DataContract([Namespace] := "http://www.sap.com/SBO/DIS")>
Public Class ProductionOrder
	<DataMember>
	Public AbsoluteEntry As Integer
	<DataMember>
	Public ProductionOrderLines As List(Of ProductionOrderLine)
End Class

<DataContract>
Public Class ProductionOrderLine
	<DataMember>
	Public LineNumber As Integer
	<DataMember>
	Public BatchNumbers As List(Of BatchNumber)
End Class

<DataContract>
Public Class BatchNumber
	<DataMember>
	Public BatchNumber As Integer
	<DataMember>
	Public Quantity As String
	<DataMember>
	Public BaseLineNumber As Integer
End Class


Затем вы сможете конвертировать xml-данные в объекты:
'this method you'll need only first time!!!
Dim sFileName As String = "FullFileName.xml"
Dim xDoc As XDocument = XDocument.Load(sFileName)
Dim xns As XNamespace = "http://www.sap.com/SBO/DIS"

Dim po As ProductionOrder =  New ProductionOrder()
po.AbsoluteEntry = xDoc.Root.Element(xns + "AbsoluteEntry").Value
po.ProductionOrderLines = New List(Of ProductionOrderLine)
For Each pol As XElement In xDoc.Descendants(xns + "ProductionOrderLine")
    Dim pl As ProductionOrderLine = New ProductionOrderLine()
    pl.LineNumber = pol.Element(xns + "LineNumber").Value
    pl.BatchNumbers = New List(Of BatchNumber)
    For Each ban As XElement In pol.Descendants(xns + "BatchNumber").Where(Function(x) x.Descendants.Count>0)
        Dim bn As BatchNumber = New BatchNumber()
        With bn
            .BatchNumber =  ban.Element(xns + "BatchNumber").Value
            .Quantity = ban.Element(xns + "Quantity").Value
            .BaseLineNumber = ban.Element(xns + "BaseLineNumber").Value
        End With
        pl.BatchNumbers.Add(bn)
    Next
    po.ProductionOrderLines.Add(pl)
Next

'now you can write your objects into proper way:
WriteObject(sFileName, po)
'read it again:
po = ReadObject(sFileName)
'gotcha!


Методы для чтения/записи объекта:

Sub WriteObject(_fileName As String, po As ProductionOrder)
    Using writer As FileStream = New FileStream(_fileName, FileMode.Create)
		Dim knownTypes() AS Type = New Type() {GetType(ProductionOrder), GetType(ProductionOrder), GetType(BatchNumber) }
		Dim dcs As DataContractSerializer = New DataContractSerializer(GetType(ProductionOrder), knownTypes)
	    dcs.WriteObject(writer, po)
	    writer.Close()
	End Using
End Sub

Function ReadObject(_fileName As String) As ProductionOrder 
	Dim po As ProductionOrder = New ProductionOrder()
	
    Using fs As FileStream = New FileStream(_fileName, FileMode.Open)
	    Dim reader As XmlDictionaryReader = XmlDictionaryReader.CreateTextReader(fs, New XmlDictionaryReaderQuotas())
		Dim knownTypes() AS Type = New Type() {GetType(ProductionOrder), GetType(ProductionOrder), GetType(BatchNumber) }
	    Dim dcs As DataContractSerializer = New DataContractSerializer(GetType(ProductionOrder), knownTypes)
	    po = DirectCast(dcs.ReadObject(reader, True), ProductionOrder)
		reader.Close()
	    fs.Close()
	End Using

	Return po
End Function


Именно так вы можете работать с xml-данными и пользовательскими объектами!


MaReBo

Спасибо за ваше решение! Все 3 предложенных способа приводят к одному и тому же результату: элементы в XML-документах, имена которых не являются уникальными, игнорируются при загрузке в dataset-object. Я думаю, что мне придется редактировать XML-файл вручную, чтобы переименовать определенные узлы. Попытался избежать этого обходного пути.

Maciej Los

См. обновленный ответ.

MaReBo

Спасибо! Я думаю, что это сработает, и я попробую это для моего класса производственного заказа. Требуется немного времени, чтобы проработать это, но в конце концов это будет стабильное решение.

Maciej Los

Всегда пожалуйста.
Овации
Мацей