Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/389.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
我可以使用Json.NET将对象序列化为JavaScript构造函数调用吗?_Javascript_C#_Serialization_Json.net - Fatal编程技术网

我可以使用Json.NET将对象序列化为JavaScript构造函数调用吗?

我可以使用Json.NET将对象序列化为JavaScript构造函数调用吗?,javascript,c#,serialization,json.net,Javascript,C#,Serialization,Json.net,我需要将一个大数据集写入一个网页(使用服务器端呈现),该网页中运行的JavaScript组件将使用该数据集。该数据由数据网格组件和图表组件的对象数组组成(因此每个数组元素都是数据网格行) 出于性能原因,我希望使用JavaScript对象构造函数而不是对象文本(有使用构造函数的对象的JIT编译器优化,并且由于省略了属性名,因此它们使用的空间更少)。我也可以在本地使用Date构造函数 以下是我现在的做法: <script type="text/javascript"> function

我需要将一个大数据集写入一个网页(使用服务器端呈现),该网页中运行的JavaScript组件将使用该数据集。该数据由数据网格组件和图表组件的对象数组组成(因此每个数组元素都是数据网格行)

出于性能原因,我希望使用JavaScript对象构造函数而不是对象文本(有使用构造函数的对象的JIT编译器优化,并且由于省略了属性名,因此它们使用的空间更少)。我也可以在本地使用
Date
构造函数

以下是我现在的做法:

<script type="text/javascript">

function WeeklySalesRow( weekName, date, totalSales, profit, loss, turnover, deltaSalesPercentage, etc )
{
    this.weekName = weekName;
    this.date = date;
    this.totalSales = totalSales;
    this.profit = profit;
    this.loss = loss;
    // etc
}

var weeklySalesData = [
@{
    Boolean first = true;
    foreach( WeeklySalesRow row in this.Model.WeeklySalesData ) {
        if( !first ) this.WriteLine( "," ); first = false;
        this.Write( "new WeeklySalesRow( \"{0}\", new Date({1}), {2}, {3}, {4}, etc )", row.WeekName, row.Date.ToUnixTimestamp(), row.TotalSales, row.Profit, row.Loss, row.Turnover, etc );
    }
}
];

function onDomContentLoaded( e ) {

    var chartCompoennt = ...
    chartComponent.loadData( weeklySalesData );
}

</script>
这比:

var weeklySalesData = [
{ weekName: "2018W1", date: "2018-01-01", totalSales: 1100, profit: 200, loss: 900, turnover: 50, deltaSalesPercentage: 0.56, etc },
{ weekName: "2018W2", date: "2018-01-08", totalSales: 1200, profit: 100, loss: 800, turnover: 50, deltaSalesPercentage: 0.56, etc },
{ weekName: "2018W3", date: "2018-01-17", totalSales: 1300, profit: 50, loss: 700, turnover: 50, deltaSalesPercentage: 0.56, etc },
{ weekName: "2018W4", date: "2018-01-23", totalSales: 1400, profit: 25, loss: 600, turnover: 50, deltaSalesPercentage: 0.56, etc },
{ weekName: "2018W5", date: "2018-02-01", totalSales: 1500, profit: 12, loss: 500, turnover: 50, deltaSalesPercentage: 0.56, etc },
];
我知道,能够直接解析JSON的运行时组件(例如,
fetch
XMLHttpRequest
)能够减轻使用对象文字表示法带来的某些性能损害(例如,解析器检查数组,发现所有对象文本共享同一组属性名,因此可以在运行时为它们定义内部隐藏基类)-但这种优化不适用于这种情况,因为数据正在呈现到SSR网页中,我知道即使是最新的性能基准也没有显示任何针对“所有数组元素看起来都一样”场景的解析器或编译器优化)


有没有一种方法可以让Json.NET自动为我实现这一点,即使用反射自动生成JavaScript构造函数和JavaScript构造函数调用?

没有完全自动的方法,但您可以使用一个调用该函数的函数或更高版本来实现这一点。有关详细信息,请参阅。通过使用.Net反射或返回的Json.Net缓存元数据,可以使自定义转换器成为泛型转换器,但如果是,则需要某种方法来确定构造函数参数顺序

例如,假设您的类型
WeeklySalesData
如下所示:

public class WeeklySalesData
{
    string weekName;
    DateTime date;
    decimal totalSales;

    // If WeeklySalesData had multiple constructors, you could mark the one to use as follows:
    // [JsonConstructor]
    public WeeklySalesData(string weekName, DateTime date, decimal totalSales)
    {
        this.weekName = weekName;
        this.date = date;
        this.totalSales = totalSales;
    }

    public string WeekName { get { return weekName; } }

    public DateTime Date { get { return date; } }

    public decimal TotalSales { get { return totalSales; } }
}
请注意,它有一个参数化构造函数,Json.NET将使用该构造函数在反序列化期间构造类型。要使用构造函数格式序列化此类类型,请首先介绍以下转换器:

public class ConstructorConverter<T> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(T) == objectType;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var contract = serializer.ContractResolver.ResolveContract(value.GetType()) as JsonObjectContract;
        if (contract == null)
            throw new JsonSerializationException(string.Format("Type {0} does not correspond to a JSON object.", value.GetType()));
        // Possibly check whether JsonObjectAttribute is applied, and use JsonObjectAttribute.Title if present.
        writer.WriteStartConstructor(value.GetType().Name);
        foreach (var provider in contract.GetConstructorParameterValueProviders())
        {
            serializer.Serialize(writer, provider.GetValue(value));
        }
        writer.WriteEndConstructor();
    }
}

public static partial class JsonExtensions
{
    internal static IEnumerable<IValueProvider> GetConstructorParameterValueProviders(this JsonObjectContract contract)
    {
        return contract.CreatorParameters.Select(p => contract.GetConstructorParameterValueProvider(p)).ToArray();
    }

    internal static IValueProvider GetConstructorParameterValueProvider(this JsonObjectContract contract, JsonProperty parameter)
    {
        if (parameter.ValueProvider != null)
            return parameter.ValueProvider;
        var property = contract.Properties.GetClosestMatchProperty(parameter.PropertyName);
        var provider = property == null ? null : property.ValueProvider;
        if (provider == null)
            throw new JsonSerializationException(string.Format("Cannot get IValueProvider for {0}", parameter));
        return provider;
    }
}
注:

  • Newtonsoft从1970年1月1日00:00:00 UTC开始,以毫秒为单位序列化
    DateTime
    构造函数,这与。您似乎正在使用不同的单位进行序列化,可能是秒而不是毫秒

  • 如果您的类型没有参数化构造函数,则可以使用其他机制(如或)指定JavaScript构造函数参数顺序。然后,可以使用与
    ObjectToArrayConverter.WriteJson()相同的算法从集合中按顺序获取构造函数参数

  • 我没有尝试实现反序列化,因为它没有被请求


演示小提琴。

我从未使用过它,但考虑到它包括
StartConstructor
EndConstructor
的条目,我想说。。。大概您是否尝试过编写自定义转换器?是否可以将构造函数的值作为列表生成?如果是这样,您可以让构造函数接受该列表,而不是
(parm1,param2,param3)
等,或者通过
apply(this,parameterArray)
调用它。但是,您确定属性名称的开销很大吗?试图忽略它们似乎是过早的优化—我希望gzip压缩有效负载可以消除传输它们时的“问题”。没有办法自动完成这项工作,但您可以使用调用的函数和稍后的
JsonWriter.writeedConstructor()
来完成。有关详细信息,请参阅。自定义转换器可以通过使用反射或
serializer.ContractResolver
实现通用化。您的问题是,Json.NET是否有办法自动为我实现这一点。。。使用反射的通用
JsonConverter
足够吗?哇!谢谢:D(关于
Date(1233)
值,它们不是真实的-我从unixtimestamp.com复制了它们,所以它们以秒为单位,而不是以毫秒为单位)。
public class ConstructorConverter<T> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(T) == objectType;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var contract = serializer.ContractResolver.ResolveContract(value.GetType()) as JsonObjectContract;
        if (contract == null)
            throw new JsonSerializationException(string.Format("Type {0} does not correspond to a JSON object.", value.GetType()));
        // Possibly check whether JsonObjectAttribute is applied, and use JsonObjectAttribute.Title if present.
        writer.WriteStartConstructor(value.GetType().Name);
        foreach (var provider in contract.GetConstructorParameterValueProviders())
        {
            serializer.Serialize(writer, provider.GetValue(value));
        }
        writer.WriteEndConstructor();
    }
}

public static partial class JsonExtensions
{
    internal static IEnumerable<IValueProvider> GetConstructorParameterValueProviders(this JsonObjectContract contract)
    {
        return contract.CreatorParameters.Select(p => contract.GetConstructorParameterValueProvider(p)).ToArray();
    }

    internal static IValueProvider GetConstructorParameterValueProvider(this JsonObjectContract contract, JsonProperty parameter)
    {
        if (parameter.ValueProvider != null)
            return parameter.ValueProvider;
        var property = contract.Properties.GetClosestMatchProperty(parameter.PropertyName);
        var provider = property == null ? null : property.ValueProvider;
        if (provider == null)
            throw new JsonSerializationException(string.Format("Cannot get IValueProvider for {0}", parameter));
        return provider;
    }
}
var data = new WeeklySalesData("2018W1", new DateTime(2019, 2, 15, 0, 0, 0, DateTimeKind.Utc), 1100);
var settings = new JsonSerializerSettings
{
    Converters = { new JavaScriptDateTimeConverter(), new ConstructorConverter<WeeklySalesData>() },
};

var json = JsonConvert.SerializeObject(new [] { data }, Formatting.Indented, settings);
[
  new WeeklySalesData(
    "2018W1",
    new Date(
      1550188800000
    ),
    1100.0
  )
]