0

I know that this question has been asked many times before, but I just can't work it out after reading all the responses. I have no clue what I'm doing wrong and looking at it for 2 days now. I have a nested class and just trying to assign values to it, but I keep getting this error. Debugging didn't bring me any further.

My class definitions:

Public Class DocumentHeader
    Private _username As String
    <JsonProperty("username")> _
    Public Property username() As String
        Get
            Return _username
        End Get
        Set(ByVal value As String)
            _username = value
        End Set
    End Property

    Private _bus_act As String
    <JsonProperty("bus-act")> _
    Public Property bus_act() As String
        Get
            Return _bus_act
        End Get
        Set(ByVal value As String)
            _bus_act = value
        End Set
    End Property

    Private _ref_doc_no As String
    <JsonProperty("ref-doc-no")> _
    Public Property ref_doc_no() As String
        Get
            Return _ref_doc_no
        End Get
        Set(ByVal value As String)
            _ref_doc_no = value
        End Set
    End Property

    Private _header_txt As String
    <JsonProperty("header-txt")> _
    Public Property header_txt() As String
        Get
            Return _header_txt
        End Get
        Set(ByVal value As String)
            _header_txt = value
        End Set
    End Property

    Private _comp_code As String
    <JsonProperty("comp-code")> _
    Public Property comp_code() As String
        Get
            Return _comp_code
        End Get
        Set(ByVal value As String)
            _comp_code = value
        End Set
    End Property

    Private _pstng_date As String
    <JsonProperty("pstng-date")> _
    Public Property pstng_date() As String
        Get
            Return _pstng_date
        End Get
        Set(ByVal value As String)
            _pstng_date = value
        End Set
    End Property

    Private _trans_date As String
    <JsonProperty("trans-date")> _
    Public Property trans_date() As String
        Get
            Return _trans_date
        End Get
        Set(ByVal value As String)
            _trans_date = value
        End Set
    End Property

    Private _fis_period As String
    <JsonProperty("fis-period")> _
    Public Property fis_period() As String
        Get
            Return _fis_period
        End Get
        Set(ByVal value As String)
            _fis_period = value
        End Set
    End Property

    Private _doc_date As String
    <JsonProperty("doc-date")> _
    Public Property doc_date() As String
        Get
            Return _doc_date
        End Get
        Set(ByVal value As String)
            _doc_date = value
        End Set
    End Property

    Private _doc_type As String
    <JsonProperty("doc-type")> _
    Public Property doc_type() As String
        Get
            Return _doc_type
        End Get
        Set(ByVal value As String)
            _doc_type = value
        End Set
    End Property
End Class

This is my main class:

Public Class RootObjectInvoice

    Private _document_header As DocumentHeader
    Public Property document_header() As DocumentHeader
        Get
            Return _document_header
        End Get
        Set(ByVal value As DocumentHeader)
            _document_header = value
        End Set
    End Property
End Class

So far, so good, I think.
Now is my code:

Dim Root As RootObjectInvoice
Dim Document_Header As DocumentHeader
Root = New RootObjectInvoice
Document_Header = New DocumentHeader
Try
    Document_Header = Root.document_header
    Document_Header.bus_act = "AAAA"

At the line Document_Header.bus_act = "AAAA" I get the NullpointerException. I'm missing something here on how to initialize the object.
Any input would be appreciated.

As this works now, I have a problem with following (I'm used to C#, not VB) I added this property to the RootObjectInvoice

Private _metadata As List(Of Metadata)
Public Property metadata() As List(Of Metadata)
    Get
        Return _metadata
    End Get
    Set(ByVal value As List(Of Metadata))
        _metadata = value
    End Set
End Property

Then try to initialize it:

Dim Meta_Data() As List(Of Metadata)
Root.metadata() = New List(Of Metadata)
Meta_Data = Root.metadata

But the last line gives me the following error:

Value of type 'System.Collections.Generic.List(Of Metadata)' cannot be converted to '1-dimensional array of System.Collections.Generic.List(Of Metadata)'.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
user9671207
  • 33
  • 2
  • 8
  • What you do is up to you but I strongly recommend using the standard naming conventions for identifiers, e.g. `documentHeader` for a local variable rather than `Document_Header` and `DocumentHeader` for a property rather than `document_header`. – jmcilhinney Oct 25 '18 at 14:18
  • Please don't add a new question to an existing post that has already been answered. If you have a new question, post a new question. – jmcilhinney Oct 25 '18 at 14:59
  • The problem is that you are declaring the `Meta_Data` variable as an array. The parentheses on the variable name mean that it is an array of the specified type, not just the specified type. You want `Dim Meta_Data As List(Of Metadata)` rather than `Dim Meta_Data() As List(Of Metadata)` – jmcilhinney Oct 25 '18 at 15:01

2 Answers2

4

Surely this line:

Document_Header = Root.document_header

should be the other way around, i.e.

Root.document_header = Document_Header

By the way, there's no point providing a full implementation of a property like this:

Private _document_header As DocumentHeader

Public Property document_header() As DocumentHeader
    Get
        Return _document_header
    End Get
    Set(ByVal value As DocumentHeader)
        _document_header = value
    End Set
End Property

when you can just do this:

Public Property document_header() As DocumentHeader

and achieve the same thing. You should only provide a full implementation if you need to do something other than get and set a backing field, e.g. validate a new value or raise an event.

jmcilhinney
  • 50,448
  • 5
  • 26
  • 46
  • 1
    Thx, in fact when I replaced it by Root.document_header = New DocumentHeader, it works. Makes sense now. Regarding the formatting, I believe I have to do it as it's an old project and I need to use VS2008 for it. Is that correct? – user9671207 Oct 25 '18 at 14:29
  • 1
    I can't recall when auto-properties were added to VB but you may be right that they are not supported in VB 2008. Do you actually have to use VB 2008 though? You can open an old project in a newer version of VS and it will generally just work. There is an upgrade process for something that old though, which will make changes to the solution and project files such that it will no longer be able to open in VS 2008. If you need that compatibility then don't upgrade it. – jmcilhinney Oct 25 '18 at 14:33
  • Indeed, I tried the inline property setting but it does not work in VS2008. I updated my initial question with another issue related to the same object. They're both defined as List OF, so why does it throw this error? Thank you – user9671207 Oct 25 '18 at 14:50
1

The DocumentHeader type is a Class, and classes are reference types. So look at this line of code:

Private _document_header As DocumentHeader

Here, _document_header is a variable for a reference type; it wants to hold a reference to an object in memory. This is the "Object reference" part of the error message. But at this time, the value of the variable is still null or Nothing. The code additionally wraps that variable with the document_header property, but that doesn't really change anything.

Later, we have this code:

Dim Root As RootObjectInvoice
Dim Document_Header As DocumentHeader
Root = New RootObjectInvoice
Document_Header = New DocumentHeader

We see a variable Root of type RootObjectInvoice. We also create an instance of that type and assign it to the variable, and we create an instance of the Document_Header header type discussed previously and assign it to a separate variable.

Now, our Root variable refers to a RootObjectInvoice instance, and the RootObjectInvoice type has a document_header property of type DocumentHeader.

But the document_header property still doesn't have a value! Creating a New instance of a reference type does not automatically create instance for reference properties or members of the type. Root.document_header is still null/Nothing.

Next we have this code:

Document_Header = Root.document_header
Document_Header.bus_act = "AAAA"

So we have a reference variable named Root.document_header that is not set to an instance of an object. We assign that empty reference to the Document_Header variable... which throws away the instance that was just created. Oops; what a waste. Then we try to access a property on that empty reference.

That's what makes things blow up and throw an exception.

You have an object reference variable named Document_Header which no longer has any value assigned. The object instance you had previously created and assigned was thrown away, and is not set anymore. Then you try to de-reference this variable to so can access a property, but there's nothing there.

So you have this question:

I'm missing something here on how to initialize the object.

You can do this:

Dim Root As New RootObjectInvoice()
Root.document_header = New DocumentHeader()

Or you can add a constructor or initializer in the RootObjectInvoice type:

Private _document_header As New DocumentHeader

I need to further address the follow up edits, because of this line:

Dim Meta_Data() As List(Of Metadata)

The extra parentheses next to Meta_Data() make this an array variable. What you declared there is an array of Lists. You just want this:

Dim Meta_Data As List(Of MetaData)

Only use parentheses for array or Property declarations. Additionally, you want this variable to actually refer to a New object. You can put this together on the same line as the declaration:

Dim Meta_Data As New List(Of Metadata)()

Just as you might write the following in C#:

var Meta_Data = new List<Metadata>();

But since you really want this to be part of the Root variable, you can skip that intermediate Meta_Data variable entirely:

Root.metadata = New List(Of Metadata)()

Just as in C# you could have written:

Root.metadata = new List<Metadata>();

But this is actually poor practice. In most cases, when a class has a collection type as a member property, that property should only provide Get access. This was true in C#, too.

You want to declare the property in the class like this:

Private _metadata As New List(Of Metadata)
Public Property metadata() As List(Of Metadata)
    Get
        Return _metadata
    End Get
End Property

You can still use the property to do everything you expect, including adding new items, even though there is no Set. The only thing you can't do is completely change the collection out from under the class; you can't assign a different List instance to the property. In the same way, it's usually better practice in C# to define a List property like this:

private List<Metadata> _metadata = new List<Metadata>();
public List<Metadata> metadata 
{
   get {return _metadata;}
}

And as of C# 6 you can even just do this:

public List<Metadata> metadata {get;} = new List<Metadata>();
Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794