In C# 5 and VS 2017, I have,
var json = JsonConvert.SerializeObject(items);
// why json is dynamic here
Clearly, SerializeObject returns string then why json is dynamic type instead of string. Yes items is dynamic type.

Clearly, SerializeObject returns string then why json is dynamic type instead of string.
Almost every operation involving a dynamic value is considered to be dynamic in turn. This includes method calls and most operators. The only exceptions are:
new Foo(someDynamicValue) is still Foo)is operator (so the compile-time type of someDynamicValue is Foo is still bool)as operator (so the compile-time type of someDynamicValue as Foo is still Foo)(Foo) someDynamicValue is still Foo)If you want the type of json to be string, just declare that explicitly:
string json = JsonConvert.SerializeObject(items);
Note that the overload of SerializeObject that's called will depend on the execution-time type of items - unlike with a statically-bound call, even overload resolution happens at execution time when any arguments are dynamic.
Of course, if you know you want to call the object overload, you could cast items instead:
var json = JsonConvert.SerializeObject((object) items);
Now the call is statically bound, and the compiler knows the return type is string.
See more on this question at Stackoverflow