1

I needed to have a Web API service with some fake data for a new PoC. The objectmodel for this is quite simple, 2 classes, where class1 has a nested array of class2. So I started building a FakeDataService where I just hardcoded some data. For brevity in this sample, I have omitted a lot of properties so the code focuses on the essence of the problem:

public class Class1
{
    public string Prop1 { get; set; }
    public IEnumerable<Class2> Prop2 { get; set; }
    public IEnumerable<string> Prop3 { get; set; }
}    

public class Class2
{
    public string Prop4 { get; set; }
}

public static class FakeDatabase
{
    public static Class1 c1instance1 = new Class1 { Prop1 = "value1", 
                          Prop2 = new Class2[] { c2instance1 }, 
                          Prop3 = new string[] { "value3" } };

    public static Class2 c2instance1 = new Class2 { Prop4 = "Value4" };

    public static Class1[] AllData = new Class1[] { c1instance1 };

}

So, my "AllData" of the fake database is an array of Class1 objects ("array" of just one in this example), and Class1 objects have a string property (prop1), an array of Class2 objects (prop2), and an array of strings property (prop3), while Class2 objects just have one string property (prop4).

The WebAPI controller is as basic as it gets:

public class ValuesController : ApiController
{

    // GET api/values
    public IEnumerable<Class1> Get()
    {
        return FakeDatabase.AllData;                                                                                                                                                                                       
    }

But when I invoke the Values controller, the result is :

<ArrayOfClass1 xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/FakeDataService.Models">
<Class1>
<Prop1>value1</Prop1>
<Prop2>
<Class2 i:nil="true"/>
</Prop2>
<Prop3 xmlns:d3p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d3p1:string>value3</d3p1:string>
</Prop3>
</Class1>
</ArrayOfClass1>

Notice how the serialization of the Class2 object yields i:nil=true

After a good few hours of searching (My example data was a LOT bigger than this simple sample here), I found that the root of the problem was the fact that the array of nested objects are defined as static instances. Eg. Change the initial code of FakeDatabase to :

public static class FakeDatabase
    {
        public static Class1 c1instance1 = new Class1 { Prop1 = "value1", 
                Prop2 = new Class2[] { new Class2 { Prop4 = "Value4" }},
                Prop3 = new string[] { "value3" } };


        public static Class1[] AllData = new Class1[] { c1instance1 };

}

Invoking the WebAPI controller now gives me (correctly) :

<ArrayOfClass1 xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/FakeDataService.Models">
<Class1>
<Prop1>value1</Prop1>
<Prop2>
<Class2>
<Prop4>Value4</Prop4>
</Class2>
</Prop2>
<Prop3 xmlns:d3p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d3p1:string>value3</d3p1:string>
</Prop3>
</Class1>
</ArrayOfClass1>

So the only difference is the array of Class2 objects. What could be the explanation of this behavior ?

dbc
  • 104,963
  • 20
  • 228
  • 340
hcd
  • 1,338
  • 2
  • 10
  • 15
  • Your output is XML so you're not using Json.NET here, you're probably using `DataContractSerializer`. But actually the issue is unrelated to the specific serializer. I updated the tags to reflect that. – dbc Jul 03 '15 at 03:19

1 Answers1

0

Your problem is that you use the c2instance1 static variable before it is initialized. From the C# Language Specification §10.4.5.1 Static field initialization:

The static field variable initializers of a class correspond to a sequence of assignments that are executed in the textual order in which they appear in the class declaration.

Thus, in your FateDatabase class, c1instance1 is used by the initializer of c1instance1 while it is still null:

public static class FakeDatabase
{
    public static Class1 c1instance1 = new Class1
    {
        Prop1 = "value1",
        Prop2 = new Class2[] { c2instance1 }, // NOT INITIALIZED YET, THUS NULL
        Prop3 = new string[] { "value3" }
    };

    public static Class2 c2instance1 = new Class2 { Prop4 = "Value4" };

    public static Class1[] AllData = new Class1[] { c1instance1 };
}

Flip the textual order of the static variables to fix the bug:

public static class FakeDatabase
{
    public static Class2 c2instance1 = new Class2 { Prop4 = "Value4" };

    public static Class1 c1instance1 = new Class1
    {
        Prop1 = "value1",
        Prop2 = new Class2[] { c2instance1 },
        Prop3 = new string[] { "value3" }
    };

    public static Class1[] AllData = new Class1[] { c1instance1 };
}
dbc
  • 104,963
  • 20
  • 228
  • 340
  • Thank you ! This was indeed the reason. Ps, the service did return JSOn, but I hadn't changed the default Content-Type yet, so Chrome displayed it as XML. – hcd Jul 05 '15 at 17:55