Home > Software engineering >  Object is nothing after json deserialization
Object is nothing after json deserialization

Time:04-22

I want to deserialize a json file into 'Artikelstammdaten' and afterwards to a list of 'Artikelstammdaten' (don't know how to iterate over the deserialized json yet). I already tried to deserialize it into the Rootobject, but the object was also "Nothing"Here is what i have tried:

 If OpenfilePath IsNot Nothing Then
        Dim asd As Artikelstammdaten
        Dim fileReader As StreamReader
        fileReader = My.Computer.FileSystem.OpenTextFileReader(OpenfilePath)
        asd = JsonConvert.DeserializeObject(Of Artikelstammdaten)(fileReader.ReadToEnd)
 End If

Edit: This is what the class structure looks like now:

Public Class Artikelstammdaten

    Public Property Artikel As String
    Public Property BezeichnungDE As String
    Public Property BezeichnungEN As String
    Public Property Einheit As String
    Public Property MatGrp As String
    Public Property Kostenart As Integer?
    Public Property Vertriebstext_DE As String
    Public Property Vertriebstext_EN As String
    Public Property Stueckliste As String
    Public Property Status As String
    Public Property Klasse As String
    Public Property Mantelflaeche As Double?
    Public Property Gewicht As Integer?
    Public Property KlasseID As String

End Class

Public Class Stueckliste
    Public Property Verkaufsartikel As String
    Public Property Position As Integer?
    Public Property PosArtikel As String
    Public Property PosBezeichnung As String
    Public Property PosKostenart As Integer?
    Public Property Datum As String
    Public Property Material As Double?
    Public Property GMK As Double?
    Public Property Lohn As Double?
    Public Property Menge As Integer?
    Public Property Mengeneinheit As String
End Class

Public Class Arbeitsgaenge

    Public Property Verkaufsartikel As String
    Public Property AGNR As Integer?
    Public Property Bereich As String
    Public Property Lohn As Double?
    Public Property Kostenstelle As Integer?
    Public Property ARBPLATZ As String
End Class

Public Class RootObject
    Public Property Artikelstammdaten As List(Of Artikelstammdaten)
    Public Property Stueckliste As List(Of Stueckliste)
    Public Property Arbeitsgaenge As List(Of Arbeitsgaenge)
End Class

Edit: I also changed the names from "ManufacturingCosts" and "MaterialCosts" to "Stueckliste" and "Arbeitsgaenge" And here is my json-File:

{"Artikelstammdaten":[{"Artikel":"VAUBEF0010"},
                    {"BezeichnungDE":"Sammelbandantrieb"},
                    {"BezeichnungEN":"Collection belt drive N50"},
                    {"Einheit":"STK"},
                    {"MatGrp":"VAU"},
                    {"Kostenart": 1500},
                    {"Vertriebstext_DE": "Antrieb, Umlenkungen"},
                    {"Vertriebstext_EN": "Drive, Deflections"},
                    {"Stueckliste":"VAUBEF0010"},
                    {"Status":"F"},
                    {"Klasse":"VTPIMV"},
                    {"Mantelflaeche":1.3},
                    {"Gewicht":120},
                    {"KlasseID":"1.2.6.5"}],
"Stueckliste": [{"Verkaufsartikel":"VAUBEF0010"},
                {"Position": 10},
                {"PosArtikel":"Z0306251"},
                {"PosBezeichnung":"VEL Elektro- Montagematerial"},
                {"PosKostenart":9105},
                {"Datum":"2022-01-31"},
                {"Material":60.41},
                {"GMK":3.63},
                {"Lohn":2.07},
                {"Menge":1},
                {"Mengeneinheit":"STK"}],
"Arbeitsgaenge": [{"Verkaufsartikel":"VAUBEF0010"},
                {"AGNR":10},
                {"Bereich":"Mechanische Montage"},
                {"Lohn":89.1},
                {"Kostenstelle":523500},{"ARBPLATZ":"K950M"}]
}

What am i doing wrong?

CodePudding user response:

I tried DeserializeObject with the json you provided


   Public Class Artikelstammdaten
      Public Property Artikel As String
      Public Property BezeichnungDE As String
      Public Property BezeichnungEN As String
      Public Property Einheit As String
      Public Property MatGrp As String
      Public Property Kostenart As Integer
      Public Property Vertriebstext_DE As String
      Public Property Vertriebstext_EN As String
      Public Property Stueckliste As String
      Public Property Status As String
      Public Property Klasse As String
      Public Property Mantelflaeche As Double
      Public Property Gewicht As Integer
      Public Property KlasseID As String
   End Class

   Public Class ManufacturingCost
      Public Property Verkaufsartikel As String
      Public Property Position As Integer
      Public Property PosArtikel As String
      Public Property PosBezeichnung As String
      Public Property PosKostenart As Integer
      Public Property Datum As String
      Public Property Material As Double
      Public Property GMK As Double
      Public Property Lohn As Double
      Public Property Menge As Integer
      Public Property Mengeneinheit As String
   End Class

   Public Class MaterialCost
      Public Property Verkaufsartikel As String
      Public Property AGNR As Integer
      Public Property Bereich As String
      Public Property Lohn As Double
      Public Property Kostenstelle As Integer
      Public Property ARBPLATZ As String
   End Class

   Public Class Root4
      Public Property Artikelstammdaten As List(Of Artikelstammdaten)
      Public Property ManufacturingCosts As List(Of ManufacturingCost)
      Public Property MaterialCosts As List(Of MaterialCost)
   End Class


   Private Sub btnJson4_Click(sender As Object, e As EventArgs)

      Dim Json As String = "{'Artikelstammdaten':[{'Artikel':'VAUBEF0010'},
                                          {'BezeichnungDE':'Sammelbandantrieb'},
                                          {'BezeichnungEN':'Collection belt drive N50'},
                                          {'Einheit':'STK'},
                                          {'MatGrp':'VAU'},
                                          {'Kostenart': 1500},
                                          {'Vertriebstext_DE': 'Antrieb, Umlenkungen'},
                                          {'Vertriebstext_EN': 'Drive, Deflections'},
                                          {'Stueckliste':'VAUBEF0010'},
                                          {'Status':'F'},
                                          {'Klasse':'VTPIMV'},
                                          {'Mantelflaeche':1.3},
                                          {'Gewicht':120},
                                            {'KlasseID':'1.2.6.5'}],
                        'ManufacturingCosts': [{'Verkaufsartikel':'VAUBEF0010'},
                                        {'Position': 10},
                                        {'PosArtikel':'Z0306251'},
                                        {'PosBezeichnung':'VEL Elektro- Montagematerial'},
                                        {'PosKostenart':9105},
                                        {'Datum':'2022-01-31'},
                                        {'Material':60.41},
                                        {'GMK':3.63},
                                        {'Lohn':2.07},
                                        {'Menge':1},
                                        {'Mengeneinheit':'STK'}],
                        'MaterialCosts': [{'Verkaufsartikel':'VAUBEF0010'},
                                        {'AGNR':10},
                                        {'Bereich':'Mechanische Montage'},
                                        {'Lohn':89.1},
                                        {'Kostenstelle':523500},{'ARBPLATZ':'K950M'}]}"

      Dim rt4 As New Root4

      rt4 = JsonConvert.DeserializeObject(Of Root4)(Json)

      Console.WriteLine(rt4.Artikelstammdaten(0).Artikel)
      Console.WriteLine(rt4.Artikelstammdaten(1).BezeichnungDE)

   End Sub

The json structure you provided and the clss you created are different.

CodePudding user response:

Your problem is that your the values for the JSON properties "Artikelstammdaten", "Stueckliste" and "Arbeitsgaenge" are not objects. They are arrays of single-property objects. This can be seen if you reformat your JSON with e.g. https://jsonlint.com/:

{
    "Artikelstammdaten": [{
            "Artikel": "VAUBEF0010"
        },
        {
            "BezeichnungDE": "Sammelbandantrieb"
        },
        {
            "BezeichnungEN": "Collection belt drive N50"
        },
        {
            "Einheit": "STK"
        },
        {
            "MatGrp": "VAU"
        },
        {
            "Kostenart": 1500
        },
        {
            "Vertriebstext_DE": "Antrieb, Umlenkungen"
        },
        {
            "Vertriebstext_EN": "Drive, Deflections"
        },
        {
            "Stueckliste": "VAUBEF0010"
        },
        {
            "Status": "F"
        },
        {
            "Klasse": "VTPIMV"
        },
        {
            "Mantelflaeche": 1.3
        },
        {
            "Gewicht": 120
        },
        {
            "KlasseID": "1.2.6.5"
        }
    ],
    "Stueckliste": [{
            "Verkaufsartikel": "VAUBEF0010"
        },
        {
            "Position": 10
        },
        {
            "PosArtikel": "Z0306251"
        },
        {
            "PosBezeichnung": "VEL Elektro- Montagematerial"
        },
        {
            "PosKostenart": 9105
        },
        {
            "Datum": "2022-01-31"
        },
        {
            "Material": 60.41
        },
        {
            "GMK": 3.63
        },
        {
            "Lohn": 2.07
        },
        {
            "Menge": 1
        },
        {
            "Mengeneinheit": "STK"
        }
    ],
    "Arbeitsgaenge": [{
            "Verkaufsartikel": "VAUBEF0010"
        },
        {
            "AGNR": 10
        },
        {
            "Bereich": "Mechanische Montage"
        },
        {
            "Lohn": 89.1
        },
        {
            "Kostenstelle": 523500
        }, {
            "ARBPLATZ": "K950M"
        }
    ]
}

You would like to deserialize those arrays of single-property objects into c# models where each nested JSON property is bound to a model property. Since this is not implemented out of the box by Json.NET, you will need to create a custom generic JsonConverter such as the following:

Public Class ObjectToDictionaryArrayConverter(Of T)
    Inherits JsonConverter

    Public Overrides Function CanConvert(ByVal objectType As Type) As Boolean
        Return objectType Is GetType(T)
    End Function

    Public Overrides ReadOnly Property CanWrite As Boolean
        Get
            Return False
        End Get
    End Property

    Public Overrides Function ReadJson(ByVal reader As JsonReader, ByVal objectType As Type, ByVal existingValue As Object, ByVal serializer As JsonSerializer) As Object
        If existingValue Is Nothing Then
            Dim contract = serializer.ContractResolver.ResolveContract(objectType)
            existingValue = contract.DefaultCreator()()
        End If

        Select Case JsonExtensions.MoveToContentAndAssert(reader).TokenType
            Case JsonToken.StartArray
                While JsonExtensions.ReadToContentAndAssert(reader).TokenType <> JsonToken.EndArray
                    Select Case reader.TokenType
                        Case JsonToken.StartObject
                            serializer.Populate(reader, existingValue)
                        Case Else
                            Throw New JsonSerializationException("Unexpected token type " & reader.TokenType.ToString())
                    End Select
                End While
            Case JsonToken.StartObject
                serializer.Populate(reader, existingValue)
            Case Else
                Throw New JsonSerializationException("Unexpected token type " & reader.TokenType.ToString())
        End Select

        Return existingValue
    End Function

    Public Overrides Sub WriteJson(ByVal writer As JsonWriter, ByVal value As Object, ByVal serializer As JsonSerializer)
        Throw New NotImplementedException()
    End Sub
End Class

Public Module JsonExtensions
    <System.Runtime.CompilerServices.Extension()>
    Public Function ReadToContentAndAssert(ByVal reader As JsonReader) As JsonReader
        Return JsonExtensions.ReadAndAssert(reader).MoveToContentAndAssert()
    End Function

    <System.Runtime.CompilerServices.Extension()>
    Public Function MoveToContentAndAssert(ByVal reader As JsonReader) As JsonReader
        If reader Is Nothing Then Throw New ArgumentNullException()
        If reader.TokenType = JsonToken.None Then reader.ReadAndAssert()       ' Skip past beginning of stream.

        While reader.TokenType = JsonToken.Comment ' Skip past comments.
            reader.ReadAndAssert()
        End While

        Return reader
    End Function

    <System.Runtime.CompilerServices.Extension()>
    Public Function ReadAndAssert(ByVal reader As JsonReader) As JsonReader
        If reader Is Nothing Then Throw New ArgumentNullException()
        If Not reader.Read() Then Throw New JsonReaderException("Unexpected end of JSON stream.")
        Return reader
    End Function
End Module

Then modify your RootObject as follows, to replace the lists with single objects:

Public Class RootObject
    Public Property Artikelstammdaten As Artikelstammdaten
    Public Property Stueckliste As Stueckliste
    Public Property Arbeitsgaenge As Arbeitsgaenge
End Class

And deserialize as follows:

Dim root as RootObject = Nothing

If OpenfilePath IsNot Nothing Then
    Dim settings = New JsonSerializerSettings() With { .Converters = New JsonConverter() { _
        New ObjectToDictionaryArrayConverter(Of Artikelstammdaten)(), _
        New ObjectToDictionaryArrayConverter(Of Stueckliste)(), _
        New ObjectToDictionaryArrayConverter(Of Arbeitsgaenge)()  } }

    Using fileReader = New StreamReader(OpenfilePath)
    Using jsonReader = New JsonTextReader(fileReader)
        root = JsonSerializer.CreateDefault(settings).Deserialize(Of RootObject)(jsonReader)
    End Using
    End Using
End If

Demo vb.net fiddle here.

Notes:

  • If you can modify your JSON format so that "Artikelstammdaten", "Stueckliste" and "Arbeitsgaenge" are objects rather than arrays of objects, the converter becomes unnecessary.

  • For performance reasons, Newtonsoft recommends reading directly from a file using a stream reader.

  • a StreamReader should always be closed via a Using statement after you are done reading from it.

  • I did not implement re-serialization of an object as an array of single-property objects because your question did not indicate it was necessary.

  • In c# ObjectToDictionaryArrayConverter<T> would be as follows:

    public class ObjectToDictionaryArrayConverter<T> : JsonConverter
    {
        public override bool CanConvert(Type objectType) => objectType == typeof(T);
    
        public override bool CanWrite => false;
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            if (existingValue == null)
            {
                var contract = serializer.ContractResolver.ResolveContract(objectType);
                existingValue = contract.DefaultCreator();
            }
    
            switch (reader.MoveToContentAndAssert().TokenType)
            {
                case JsonToken.StartArray:
                    {
                        while (reader.ReadToContentAndAssert().TokenType != JsonToken.EndArray)
                        {
                            switch (reader.TokenType)
                            {
                                case JsonToken.StartObject:
                                    serializer.Populate(reader, existingValue);
                                    break;
                                default:
                                    throw new JsonSerializationException("Unexpected token type "   reader.TokenType.ToString());
                            }
                        }
                    }
                    break;
    
                case JsonToken.StartObject:
                    serializer.Populate(reader, existingValue);
                    break;
    
                default:
                    throw new JsonSerializationException("Unexpected token type "   reader.TokenType.ToString());
            }
            return existingValue;
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
    }
    
    public static partial class JsonExtensions
    {
        public static JsonReader ReadToContentAndAssert(this JsonReader reader) =>
            reader.ReadAndAssert().MoveToContentAndAssert();
    
        public static JsonReader MoveToContentAndAssert(this JsonReader reader)
        {
            if (reader == null)
                throw new ArgumentNullException();
            if (reader.TokenType == JsonToken.None)       // Skip past beginning of stream.
                reader.ReadAndAssert();
            while (reader.TokenType == JsonToken.Comment) // Skip past comments.
                reader.ReadAndAssert();
            return reader;
        }
    
        public static JsonReader ReadAndAssert(this JsonReader reader)
        {
            if (reader == null)
                throw new ArgumentNullException();
            if (!reader.Read())
                throw new JsonReaderException("Unexpected end of JSON stream.");
            return reader;
        }
    }
    

    c# demo fiddle here.

  • Related