davearia Posted April 3, 2008 Posted April 3, 2008 Hi, I am now trying deserialize my employee objects in my XML document into an instance of a class. Here is the class I wish to create an instance of: Public Class EmployeeAdder #Region "Declarations" Private _ID As Int32 = 0 Private _Name As String = String.Empty Private _ActivityID As Int32 = 0 #End Region #Region "Properties" Public Property ID() As Int32 Get Return _ID End Get Set(ByVal value As Int32) _ID = value End Set End Property Public Property Name() As String Get Return _Name End Get Set(ByVal value As String) _Name = value End Set End Property Public Property ActivityID() As Int32 Get Return _ActivityID End Get Set(ByVal value As Int32) _ActivityID = value End Set End Property #End Region End Class Here is the XML document I am using: <Employees xmlns="http://www.me.com"> <Employee> <ID>11</ID> <Name>Dave Jones</Name> <ActivityID>9</ActivityID> </Employee> <Employee> <ID>111</ID> <Name>Wayne Wallice</Name> <ActivityID>1</ActivityID> </Employee> <Employee> <ID>1111</ID> <Name>Justin Davies</Name> <ActivityID>2</ActivityID> </Employee> <Employee> <ID>11111</ID> <Name>Matthew Jones</Name> <ActivityID>0</ActivityID> </Employee> <Employee> <ID>111111</ID> <Name>Steve Pear</Name> <ActivityID>2</ActivityID> </Employee> </Employees> Finally here is my function that I am trying to get to work: Private Function AddEmployee(ByVal xmlDoc As XmlDocument) As Boolean 'Get root node and process its children. Try Dim xmlNodeEmployees As XmlNode = xmlDoc.FirstChild For Each node As XmlNode In xmlNodeEmployees.ChildNodes Dim buffer() As Byte = System.Text.ASCIIEncoding.ASCII.GetBytes(node.InnerXml) Dim memstrm As New System.IO.MemoryStream(buffer) Dim xmlReader As New XmlTextReader(memstrm) Dim employeeAdder As New EmployeeAdder Dim serializer As New XmlSerializer(GetType(EmployeeAdder)) employeeAdder = CType(serializer.Deserialize(xmlReader), EmployeeAdder) Next Catch ex As Exception Return False End Try End Function When it runs it get as far as employeeAdder = CType(serializer.Deserialize(xmlReader), EmployeeAdder) and then throws this exception: InnerException = {"<ID xmlns='http://www.me.com'> was not expected."} Does anyone know what I am doing wrong here? Thanks, Dave. Quote
Machaira Posted April 3, 2008 Posted April 3, 2008 Does anyone know what I am doing wrong here? You're trying to deserialize a list of objects into a single object. It seems list you're also using XML that you created, not XML created by the serialization process. Try: Dim lst As New List(Of EmployeeAdder) Dim ser As New Xml.Serialization.XmlSerializer(GetType(List(Of EmployeeAdder))) Dim reader As New System.IO.StreamReader(New System.IO.FileStream(Application.StartupPath & "\test.xml", IO.FileMode.Open)) lst = CType(ser.Deserialize(reader), List(Of EmployeeAdder)) using XML formatted like this: <?xml version="1.0" encoding="utf-8"?> <ArrayOfEmployeeAdder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <EmployeeAdder> <ID>1</ID> <Name>Test</Name> <ActivityID>1</ActivityID> </EmployeeAdder> <EmployeeAdder> <ID>2</ID> <Name>Test 2</Name> <ActivityID>2</ActivityID> </EmployeeAdder> </ArrayOfEmployeeAdder> Quote Here's what I'm up to.
davearia Posted April 4, 2008 Author Posted April 4, 2008 Hi, Sorry Machaira I didn't get back to you sooner. I am not trying to deserialize all of the objects at once. The code is getting all of the child nodes of the employees root node and creating an instance of each employee. As this XML comes from a file already created, I didn't really want to use XML created by serialization. I assume this is possible? I have updated the method, and have a temporary fix to it by using X-Path to interrigate the nodes. But it would be much nicer to deserialize straight from the XML document. The issue seems to be with the namespace but I cannot see what it is that is wrong. Please help if you can. Many thanks, Dave. Quote
Machaira Posted April 4, 2008 Posted April 4, 2008 If you're using an XML format that the serializer can use you can't use serialization. Since you've already go the XMLDocument object you should just be able to iterate through the nodes and populate the properties of an employee object. I think you're making it harder than it needs to be by introducing things like the stream and XmlTextReader. Try this: Dim lst As New List(Of EmployeeAdder) Dim emp As EmployeeAdder Dim doc As New Xml.XmlDocument Dim node As Xml.XmlNode doc.Load(Application.StartupPath & "\test.xml") For Each node In doc.DocumentElement emp = New EmployeeAdder emp.ActivityID = Convert.ToInt32(node.SelectSingleNode("descendant::ActivityID").InnerText) emp.ID = Convert.ToInt32(node.SelectSingleNode("descendant::ID").InnerText) emp.Name = node.SelectSingleNode("descendant::Name").InnerText lst.Add(emp) Next node If you have to have the "xmlns="http://www.me.com", you'll need to add the code to recognize it. Quote Here's what I'm up to.
davearia Posted April 8, 2008 Author Posted April 8, 2008 Hi, I have already coded it to iterate through the document like you described and it works just fine. I would like to be able to manage the same task using deserialization instead for future projects etc. I have Googled this quite a lot and it does seem like the class I am trying to create by deserializing the XML has to reference this namspace or else they do not match, giving this error. Reading articles like http://www.microsoft.com/belux/msdn/nl/community/columns/jdruyts/wsproxy.mspx it appears in C# there is a way of adding attributes to classes and their properties to acheive this. However I cannot find a V.B. equivalent. Looking the MSDN I found this: ms-help://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.NETDEVFX.v20.en/CPref19/html/C_System_Xml_Serialization_XmlTypeAttribute_ctor.htm This lead me to this adoptation: Private Sub AddEmployee(ByVal xmlDoc As XmlDocument, ByVal requestID As Int32) Dim nsm As New System.Xml.XmlNamespaceManager(xmlDoc.NameTable) nsm.AddNamespace("ns", Resources.Resource.AddEmployeeNameSpace) 'Get root node and process its children. Dim xmlNodeEmployees As XmlNode = xmlDoc.SelectSingleNode("ns:Employees", nsm) For Each node As XmlNode In xmlNodeEmployees.ChildNodes Dim buffer() As Byte = System.Text.ASCIIEncoding.ASCII.GetBytes(node.OuterXml) Dim memstrm As New System.IO.MemoryStream(buffer) Dim xmlReader As New XmlTextReader(memstrm) Dim employeeAdder As New EmployeeAdder Dim employeeOverride As New XmlAttributeOverrides() Dim employeeAttributes As New XmlAttributes() Dim employeeType As New XmlTypeAttribute() employeeType.TypeName = "employeeAdder" employeeType.Namespace = Resources.Resource.AddEmployeeNameSpace employeeAttributes.XmlType = employeeType employeeOverride.Add(GetType(EmployeeAdder), employeeAttributes) Dim serializer As New XmlSerializer(GetType(EmployeeAdder), employeeOverride) Try employeeAdder = CType(serializer.Deserialize(xmlReader), EmployeeAdder) Catch ex As Exception Dim str As String = ex.ToString Finally memstrm.Close() End Try Next End Sub But I just get the same error as I got at the start of this post. Can anyone help? Thanks, Dave. Quote
Administrators PlausiblyDamp Posted April 8, 2008 Administrators Posted April 8, 2008 Under vb the same attributes should work, you just enclose them in angle brackets rather than square ones. i.e. for the c# [XmlAttribute("Test")] public int Sample; the vb would be _ Public Sample as Integer Quote Posting Guidelines FAQ Post Formatting Intellectuals solve problems; geniuses prevent them. -- Albert Einstein
davearia Posted April 8, 2008 Author Posted April 8, 2008 Hi, So where and how do I add these to the following class to give this class the namespace? I m only asking because I am starting to get lost. I tried quite a few attempts with the angle brackets before and after the last post with no luck. Any help would be appreciated! #Region "Imports" Imports System.Xml #End Region Public Class EmployeeAdder #Region "Declarations" Private _KronosID As Int32 = 0 Private _Name As String = String.Empty Private _ActivityID As Int32 = 0 #End Region #Region "Properties" Public Property KronosID() As Int32 Get Return _KronosID End Get Set(ByVal value As Int32) _KronosID = value End Set End Property Public Property Name() As String Get Return _Name End Get Set(ByVal value As String) _Name = value End Set End Property Public Property ActivityID() As Int32 Get Return _ActivityID End Get Set(ByVal value As Int32) _ActivityID = value End Set End Property #End Region End Class Quote
Machaira Posted April 8, 2008 Posted April 8, 2008 Public Class EmployeeAdder <XmlAttribute("KronosID")> _ Public KronosID As Int32 = 0 <XmlAttribute("Name")> _ Public Name As String = String.Empty <XmlAttribute("ActivityID")> _ Public ActivityID As Int32 = 0 End Class Since you're not doing any validation in the property accessors just make the members public. I still don't understand the reasoning behind making things more difficult than you need to. Quote Here's what I'm up to.
davearia Posted April 8, 2008 Author Posted April 8, 2008 Hi, If I put the above code into my solution including import System.XML I get the error: 'XmlAttribute' cannot be used as an attribute because it does not inherit from 'System.Attribute'. I appreciate you thinking I am making this harder than it needs be. But the reason for wanting to get this to work is so that in future I can deserialize xml with namspaces into objects etc. Many thanks to everyone especially Machaira for their help so far. Any ideas what this error is caused by? Cheers, Dave. Quote
Machaira Posted April 8, 2008 Posted April 8, 2008 You should be able to use the code I gave in post #4, you just need to take the namespace into account. Maybe I'm just missing something about exactly why you're trying to do it the way you're doing it. :( Quote Here's what I'm up to.
davearia Posted April 9, 2008 Author Posted April 9, 2008 Hi Machaira, Okay, let me explain why I want to acheive this the way I am trying to. This code is part of a small web service I have wrote. The service currently serves 3 public functions that, adds an employee, updates an employee, deletes an employee. Each takes an xml document as a parameter. Each of the calls firstly validates the xml document passed in. This is why the namespace is included in the xml, to tie the xml to to XSD stored on the server. If the xml does not validate the service exits, if it validates then the process continues. Currently I am doing the process in the way you suggested in post #4 in that I am iterating through the nodes and getting values for name and ID etc to populate an instance of the class. This is fine and works well, but lets say there are not 3 possible classes we need to create and therefore have to iterate through the xml structure, but 30. That means I would need to write 30 routines that iterate through each specific node structure. Although this is possible I think the following is a more desired fix. We have a generic sub that gets passed the xml document and enum that tells the sub what the request type something like the following: Private Sub AddEmployee(ByVal xmlDoc As XmlDocument, ByVal requestID As Int32) Dim nsm As New System.Xml.XmlNamespaceManager(xmlDoc.NameTable) Dim obj As New Object Select Case requestID Case requestType.insert obj = New EmployeeAdder nsm.AddNamespace("ns", Resources.Resource.AddEmployeeNameSpace) Case requestType.update obj = New EmployeeUpdater nsm.AddNamespace("ns", Resources.Resource.UpdateEmployeeNameSpace) Case requestType.delete obj = New EmployeeDeleter nsm.AddNamespace("ns", Resources.Resource.DeleteEmployeeNameSpace) End Select 'Get root node and process its children. Dim xmlNodeEmployees As XmlNode = xmlDoc.SelectSingleNode("ns:Employees", nsm) For Each node As XmlNode In xmlNodeEmployees.ChildNodes 'Deserialize somehow? Dim result As Int32 = _BusinessServices.AddEmployee(obj) Next End Sub Once the code gets the correct namespace at the start of the sub we can also create our class as well, it would then be sensible/generic to use the current xml node to deserialize this to create instance of this class. This approach to me seems a pain to get to work initially, but in the long run so much easier to maintain etc. I hope that I have made more sense this time. I know once I get into something it is very easy to assume that everyone else knows what your'e trying to do. :D Thanks for all your help so far, Dave. :D Quote
Administrators PlausiblyDamp Posted April 13, 2008 Administrators Posted April 13, 2008 If there is a valid xsd for the xml file then you could try the xsd.exe command line tool. xsd.exe /c /l:vb should work Quote Posting Guidelines FAQ Post Formatting Intellectuals solve problems; geniuses prevent them. -- Albert Einstein
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.