How to serialize only public members?

cjard

Well-known member
Joined
Apr 25, 2006
Messages
7,081
Programming Experience
10+
So.. I got the basic XmlSerializer to produce me multiple identically named nodes without putting the array wrapper around them, thanks to john for that..

Now, I've just looked closer at one of the sample requests I'm expected to form and I see this:

VB.NET:
<EXPERIAN>
      <RESY> 
            <NAMESCOUNT>2</NAMESCOUNT> 
[COLOR="Magenta"]            <NAMESEQUENCE>01</NAMESEQUENCE> 
            <DATEFROM_CCYY>2000</DATEFROM_CCYY> 
            <DATEFROM_MM>01</DATEFROM_MM> 
            <DATEFROM_DD>01</DATEFROM_DD> 
            <DATETO_CCYY>2004</DATETO_CCYY> 
            <DATETO_MM>01</DATETO_MM> 
            <DATETO_DD>01</DATETO_DD> [/COLOR]
[COLOR="Red"]            <NAMESEQUENCE>11</NAMESEQUENCE> 
            <DATEFROM_CCYY>2000</DATEFROM_CCYY> 
            <DATEFROM_MM>01</DATEFROM_MM> 
            <DATEFROM_DD>01</DATEFROM_DD> 
            <DATETO_CCYY>2004</DATETO_CCYY> 
            <DATETO_MM>01</DATETO_MM> 
            <DATETO_DD>01</DATETO_DD> [/COLOR]
      </RESY> 
      <RESY> 
            <NAMESCOUNT>2</NAMESCOUNT> 
            <NAMESEQUENCE>01</NAMESEQUENCE> 
            <DATEFROM_CCYY>1995</DATEFROM_CCYY> 
            <DATEFROM_MM>01</DATEFROM_MM> 
            <DATEFROM_DD>01</DATEFROM_DD> 
            <DATETO_CCYY>2000</DATETO_CCYY> 
            <DATETO_MM>01</DATETO_MM> 
            <DATETO_DD>01</DATETO_DD> 
            <NAMESEQUENCE>11</NAMESEQUENCE> 
            <DATEFROM_CCYY>1995</DATEFROM_CCYY> 
            <DATEFROM_MM>01</DATEFROM_MM> 
            <DATEFROM_DD>01</DATEFROM_DD> 
            <DATETO_CCYY>2000</DATETO_CCYY> 
            <DATETO_MM>01</DATETO_MM> 
            <DATETO_DD>01</DATETO_DD> 
      </RESY> 
</EXPERIAN>

:confused: I haven't got a clue how I'm going to make the default serializer output this.. I have a RESYBlock class that I can array and it will produce multiple RESY blocks.. but I'm thinking I'll have to extract some of the properties it has into a RESY_SUBInline class because within a resy block, some elements repeat with different values (again, the meaning is derived from the order) and I will somehow have to prevent the RESY_SUBInline from outputting as a tag.

Here's the existing code:
VB.NET:
<System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.42")> _ 
<System.SerializableAttribute()> _ 
<System.Diagnostics.DebuggerStepThroughAttribute()> _ 
<System.ComponentModel.DesignerCategoryAttribute("code")> _ 
Public Partial Class ExperianRESYBlock 
    
    Private nAMESCOUNTField As String 
    
[COLOR="SeaGreen"]    Private nAMESEQUENCEField As Resy_NameSequence 
    
    Private rESY_DATEFROM_CCYYField As String 
    
    Private rESY_DATEFROM_MMField As String 
    
    Private rESY_DATEFROM_DDField As String 
    
    Private rESY_DATETO_CCYYField As String 
    
    Private rESY_DATETO_MMField As String 
    
    Private rESY_DATETO_DDField As String 
    
    <System.Xml.Serialization.XmlElementAttribute("NAMESCOUNT")> _ 
    Public Property NamesCount() As String 
        ...yada...
    End Property 
    
    <System.Xml.Serialization.XmlElementAttribute("NAMESEQUENCE")> _ 
    Public Property NameSequence() As Resy_NameSequence 
        ...yada...
    End Property 
    
    <System.Xml.Serialization.XmlElementAttribute("RESY_DATEFROM_CCYY")> _ 
    Public Property DateFromYYYY() As String 
        ...yada...
    End Property 
    
    <System.Xml.Serialization.XmlElementAttribute("RESY_DATEFROM_MM")> _ 
    Public Property DateFromMM() As String 
        ...yada...
    End Property 
    
    <System.Xml.Serialization.XmlElementAttribute("RESY_DATEFROM_DD")> _ 
    Public Property DateFromDD() As String 
        ...yada...
    End Property 
    
    <System.Xml.Serialization.XmlElementAttribute("RESY_DATETO_CCYY")> _ 
    Public Property DateToYYYY() As String 
        ...yada...
    End Property 
    
    <System.Xml.Serialization.XmlElementAttribute("RESY_DATETO_MM")> _ 
    Public Property DateToMM() As String 
        ...yada...
    End Property 
    
    <System.Xml.Serialization.XmlElementAttribute("RESY_DATETO_DD")> _ 
    Public Property DateToDD() As String 
        ...yada...
    End Property 
    [/COLOR]
End Class

In green will probably have to be pulled out to its own class, so that a RESY can contain an array of this class, but then how to just put the elements of the array into the XML without a nesting tag at all?

i.e. if we do this:
VB.NET:
Public Partial Class ExperianRESYBlock 
    
    Private nAMESCOUNTField As String 

    <System.Xml.XmlElement("oops!")> _
    Public experianLameHack(0 to 1) As New ExperianRESY_SUBInline

End Class

Public Partial Class ExperianRESY_SUBInline

  ... namesequence.. datefrom.. dateto...

End Class

Is going to serialize like this:

VB.NET:
<RESY>
  <NAMESCOUNT>2<?NAMESCOUNT>
  <oops!>
    <NAMESEQUENCE>0</NAMESEQUENCE>
    <DATEFROM.. blah>
  </oops!>
  <oops!>
    <NAMESEQUENCE>1</NAMESEQUENCE>
    <DATEFROM.. blah>
  </oops!>
</RESY>

when none of the oops tags should be present (magenta and red blocks from first code section)


Looking at the XPaths they gave:

VB.NET:
/EXPERIAN/RESY/NAMESDATA/NAMESEQUENCE

/EXPERIAN/RESY/NAMESDATA/DATEFROM_CCYY
/EXPERIAN/RESY/NAMESDATA/DATEFROM_MM
/EXPERIAN/RESY/NAMESDATA/DATEFROM_DD

/EXPERIAN/RESY/NAMESDATA/DATETO_CCYY
/EXPERIAN/RESY/NAMESDATA/DATETO_MM
/EXPERIAN/RESY/NAMESDATA/DATETO_DD

We see the referenced node "NAMESDATA" which I've called RESY_SUBInline isnt even present in the example XML they gave which is apparently a direct copy/paste out of their fully working test harness.
-

I think I'm on a hiding to nothing with this stupid product. They dont get my vote as Company of the Year.


So.. the question is, i guess, cant we make the default serializer serialize just the public elements of an object it finds in an array, without adding a nesting tag for the array itself?
 
Last edited:
:confused: I haven't got a clue how I'm going to make the default serializer output this..
It can't as far as I can see. IXmlSerializer can do it, where you write custom serialization code. Have a look at the example classes here with this layout:
- EXPERIAN contains a collection of RESY
- RESY contains a collection of SEQUENCE
- SEQUENCE contains some fields.
There is not much job for the reader/writer of each class.
VB.NET:
Class EXPERIAN
    Implements Xml.Serialization.IXmlSerializable

    Public RESYs As New List(Of RESY)

    Public Function GetSchema() As System.Xml.Schema.XmlSchema _
    Implements System.Xml.Serialization.IXmlSerializable.GetSchema
        Return Nothing
    End Function

    Public Sub ReadXml(ByVal reader As System.Xml.XmlReader) _
    Implements System.Xml.Serialization.IXmlSerializable.ReadXml
        Try
            reader.ReadStartElement("EXPERIAN")
            Do
                Dim r As New RESY
                DirectCast(r, Xml.Serialization.IXmlSerializable).ReadXml(reader)
                RESYs.Add(r)
            Loop
        Catch ex As Exception
        End Try
    End Sub

    Public Sub WriteXml(ByVal writer As System.Xml.XmlWriter) _
    Implements System.Xml.Serialization.IXmlSerializable.WriteXml
        For Each r As RESY In RESYs
            DirectCast(r, Xml.Serialization.IXmlSerializable).WriteXml(writer)
        Next
    End Sub
End Class

Class RESY
    Implements Xml.Serialization.IXmlSerializable

    Public seq As New List(Of SEQUENCE)

    Public Function GetSchema() As System.Xml.Schema.XmlSchema _
    Implements System.Xml.Serialization.IXmlSerializable.GetSchema
        Return Nothing
    End Function

    Public Sub ReadXml(ByVal reader As System.Xml.XmlReader) _
    Implements System.Xml.Serialization.IXmlSerializable.ReadXml
        reader.ReadStartElement("RESY")
        For i As Integer = 1 To reader.ReadElementContentAsInt
            Dim s As New SEQUENCE
            DirectCast(s, Xml.Serialization.IXmlSerializable).ReadXml(reader)
            seq.Add(s)
        Next
        reader.ReadEndElement()
    End Sub

    Public Sub WriteXml(ByVal writer As System.Xml.XmlWriter) _
    Implements System.Xml.Serialization.IXmlSerializable.WriteXml
        writer.WriteStartElement("RESY")
        writer.WriteElementString("NAMESCOUNT", seq.Count)
        For Each s As SEQUENCE In seq
            DirectCast(s, Xml.Serialization.IXmlSerializable).WriteXml(writer)
        Next
        writer.WriteEndElement()
    End Sub
End Class

Class SEQUENCE
    Implements Xml.Serialization.IXmlSerializable

    Public NAMESEQUENCE As Integer
    Public DATEFROM_CCYY As Integer

    Public Function GetSchema() As System.Xml.Schema.XmlSchema _
    Implements System.Xml.Serialization.IXmlSerializable.GetSchema
        Return Nothing
    End Function

    Public Sub ReadXml(ByVal reader As System.Xml.XmlReader) _
    Implements System.Xml.Serialization.IXmlSerializable.ReadXml
        NAMESEQUENCE = reader.ReadElementContentAsInt
        DATEFROM_CCYY = reader.ReadElementContentAsInt
    End Sub

    Public Sub WriteXml(ByVal writer As System.Xml.XmlWriter) _
    Implements System.Xml.Serialization.IXmlSerializable.WriteXml
        writer.WriteElementString("NAMESEQUENCE", NAMESEQUENCE)
        writer.WriteElementString("DATEFROM_CCYY", DATEFROM_CCYY)
    End Sub
End Class
the test code here writes out a file similar to the posted xml data, the reader gets it back:
VB.NET:
Sub writeRESY()
    Dim fs As New IO.FileStream("resy2.xml", IO.FileMode.Create, IO.FileAccess.Write)
    Dim ser As New Xml.Serialization.XmlSerializer(GetType(EXPERIAN))
    Dim xp As New EXPERIAN
    Dim r As New RESY
    r.seq.Add(New SEQUENCE)
    r.seq.Add(New SEQUENCE)
    xp.RESYs.Add(r)
    r = New RESY
    r.seq.Add(New SEQUENCE)
    r.seq.Add(New SEQUENCE)
    r.seq.Add(New SEQUENCE)
    xp.RESYs.Add(r)
    ser.Serialize(fs, xp)
    fs.Close()
    Process.Start("notepad", "resy2.xml")
End Sub

Sub readRESY()
    Dim fs As New IO.FileStream("resy2.xml", IO.FileMode.Open, IO.FileAccess.Read)
    Dim ser As New Xml.Serialization.XmlSerializer(GetType(EXPERIAN))
    Dim xa As EXPERIAN = ser.Deserialize(fs)
    fs.Close()
End Sub
 
Back
Top