前端妹子如何在 sqlserver 2008 中如何用自定义函数 解析json数据

前言:

需求是这样的,女神遇到了一个具体问题。就是sqlserver数据库中有些数据是json字符串。
但是由于她们公司的sqlserver数据库版本比较低,没有自带的解析方法。
所以需要用自定义函数查询。

开始干活:

0.预告

我在本帖子中会创建两个自定义函数。
一个名字叫做parseJSON是通用json解析函数。另外一个需要根据业务情况,为带有json的业务表做一个专用的自定义函数。

1.首先先建立一个“通用的json解析自定义函数”。(这个代码是网络上找到的成熟代码)

直接粘贴代码

CREATE FUNCTION [dbo].[parseJSON] ( @JSON NVARCHAR(MAX) )
。。。。
这个自定义函数内容太多了,为了阅读方便,我贴在帖子尾部了。
END

2. 重点讲解一下 函数 “parseJSON( )”的用法

首先通过代码阅读了解到,函数 “parseJSON( )”的返回结果是一个表(结果集)。
我们可以通过sql测试它。
假设我们要解析的json是:

{"channelCode":"A003","random":"124317f0-8e42-4c9c-88b0-cabacc8a8079"}

我们可以直接用sql对这个json字符串进行解析。
函数 “parseJSON( )”的参数要求“输入json字符串”,位置放在 “from”后面当作 “表”用。一定要加 “dbo.”
代码如下

select *
from dbo.parseJSON('{"channelCode":"A003","random":"124317f0-8e42-4c9c-88b0-cabacc8a8079"}')
;

得到的结果如下图是一个固定结构的表。
在这里插入图片描述
由此可以看到,json被打开,内部的key和value分别放在两个不同的列。
我们可以根据where条件去查询它,比如如下语句:

select StringValue
from dbo.parseJSON('{"channelCode":"A003","random":"124317f0-8e42-4c9c-88b0-cabacc8a8079"}')
where NAME = 'channelCode'
;

得到结果是:
在这里插入图片描述

3.学会了函数 “parseJSON( )”的用法,那么如何方便的在以后的查询语句中使用它呢?

由于函数 “parseJSON( )”的返回结果是一个表。而我们实际上多数情况下希望在select中向查询普通字段一样查询json内的参数。
所以我们需要用一些“土办法”。
这里我选择的方案是,针对具体的业务表,做一个专用与这个表以后查询的专用函数。

预先准备测试用的数据表和数据

教学用测试表的表结构:

-- 创建一个带有json数据的表测试表table_j
create table table_j
(
    id  int not null,
    val varchar(800)
)
go

教学用测试数据:

-- 插入3条测试数据
INSERT INTO dbo.table_j (id, val)
VALUES (1, N'{"channelCode":"A001","random":"5e590a2b-a295-4ac6-af0b-92528a84a3ee"}');
INSERT INTO dbo.table_j (id, val)
VALUES (2, N'{"channelCode":"A002","random":"de823b5a-19db-4492-a144-2f66945b1ce4"}');
INSERT INTO dbo.table_j (id, val)
VALUES (3, N'{"channelCode":"A003","random":"124317f0-8e42-4c9c-88b0-cabacc8a8079"}');

开始制作“专用函数”

我们设计一个函数 叫 “ parseJSON_table_j( ) ”,
设计他有两个参数,
参数1 用来做查询数据的唯一id。
参数2 用来告诉我们要查json内的哪个值。
(这里假定我们是知道表结构并知道哪个字段存储了json数据。)

-- 为了方便使用 专门为测试表测试表table_j 做了第二个自定义函数
-- 它的功能是 通过测试表table_j的唯一key 查询对应json数据里面 某一个节点的值
-- 返回字符串类型
CREATE FUNCTION dbo.parseJSON_table_j(
    @param1_id INT, -- 唯一标识
    @param2_jsonkey nvarchar(max) -- json中的节点名称
)
    RETURNS nvarchar(max)
AS
BEGIN

    DECLARE @json nvarchar(max)
    DECLARE @result nvarchar(max)
    
	-- 这里直接硬编码写死了(表名 table_j )和(存放json字符串的字段 val),在实际使用中要根据查询json所在表进行修改。 
    select @json = val from table_j where id = @param1_id

    select @result = StringValue
    from dbo.parseJSON(@json)
    where NAME = @param2_jsonkey

    RETURN @result
END

如何使用“专用函数”

直接通过传参是方式可以查询。比如下面的sql脚本:

-- 查一下试试
SELECT id,
       val,
       dbo.parseJSON_table_j(id, 'channelCode') as channelCode,
       dbo.parseJSON_table_j(id, 'random')      as random
from table_j
;

得到的结果如下,完美的把json字符串解析到了查询语句中。
在这里插入图片描述

最后,上价值!!!

我家女神天下第一美,不接受任何反驳。

女神的毒唯,爱宠,我家小公主,妞妞镇楼。(话说谁能告诉我毒唯是什么。。。。。。。。)

在这里插入图片描述

附录:函数 “parseJSON( )”的代码

CREATE FUNCTION [dbo].[parseJSON](@JSON NVARCHAR(MAX))
    RETURNS @hierarchy TABLE
                       (
                           element_id  INT IDENTITY (1, 1)
                                                     NOT NULL, /* internal surrogate primary key gives the order of parsing and the list order */
                           sequenceNo  [int]         NULL, /* the place in the sequence for the element */
                           parent_ID   INT,/* if the element has a parent then it is in this column. The document is the ultimate parent, so you can get the structure from recursing from the document */
                           Object_ID   INT,/* each list or object has an object id. This ties all elements to a parent. Lists are treated as objects here */
                           NAME        NVARCHAR(2000),/* the name of the object */
                           StringValue NVARCHAR(MAX) NOT NULL,/*the string representation of the value of the element. */
                           ValueType   VARCHAR(10)   NOT NULL /* the declared type of the value represented as a string in StringValue*/
                       )
AS
BEGIN
    DECLARE @FirstObject INT , --the index of the first open bracket found in the JSON string
        @OpenDelimiter INT ,--the index of the next open bracket found in the JSON string
        @NextOpenDelimiter INT ,--the index of subsequent open bracket found in the JSON string
        @NextCloseDelimiter INT ,--the index of subsequent close bracket found in the JSON string
        @Type NVARCHAR(10) ,--whether it denotes an object or an array
        @NextCloseDelimiterChar CHAR(1) ,--either a '}' or a ']'
        @Contents NVARCHAR(MAX) , --the unparsed contents of the bracketed expression
        @Start INT , --index of the start of the token that you are parsing
        @end INT ,--index of the end of the token that you are parsing
        @param INT ,--the parameter at the end of the next Object/Array token
        @EndOfName INT ,--the index of the start of the parameter at end of Object/Array token
        @token NVARCHAR(max) ,--either a string or object
        @value NVARCHAR(MAX) , -- the value as a string
        @SequenceNo INT , -- the sequence number within a list
        @name NVARCHAR(200) , --the name as a string
        @parent_ID INT ,--the next parent ID to allocate
        @lenJSON INT ,--the current length of the JSON String
        @characters NCHAR(36) ,--used to convert hex to decimal
        @result BIGINT ,--the value of the hex symbol being parsed
        @index SMALLINT ,--used for parsing the hex value
        @Escape INT --the index of the next escape character


    DECLARE @Strings TABLE /* in this temporary table we keep all strings, even the names of the elements, since they are 'escaped' in a different way, and may contain, unescaped, brackets denoting objects or lists. These are replaced in the JSON string by tokens representing the string */
                     (
                         String_ID   INT IDENTITY (1, 1),
                         StringValue NVARCHAR(MAX)
                     )
    SELECT--initialise the characters to convert hex to ascii
          @characters = '0123456789abcdefghijklmnopqrstuvwxyz',
          @SequenceNo = 0, --set the sequence no. to something sensible.
        /* firstly we process all strings. This is done because [{} and ] aren't escaped in strings, which complicates an iterative parse. */
          @parent_ID = 0;
    WHILE 1 = 1 --forever until there is nothing more to do
        BEGIN
            SELECT @start = PATINDEX('%[^a-zA-Z]["]%',
                                     @json COLLATE SQL_Latin1_General_CP850_Bin);--next delimited string
            IF @start = 0
                BREAK --no more so drop through the WHILE loop
            IF SUBSTRING(@json, @start + 1, 1) = '"'
                BEGIN
                    --Delimited Name
                    SET @start = @Start + 1;
                    SET @end = PATINDEX('%[^\]["]%',
                                        RIGHT(@json,
                                              LEN(@json + '|') - @start) COLLATE SQL_Latin1_General_CP850_Bin);
                END
            IF @end = 0 --no end delimiter to last string
                BREAK --no more
            SELECT @token = SUBSTRING(@json, @start + 1, @end - 1)
            --now put in the escaped control characters
            SELECT @token = REPLACE(@token, FROMString, TOString)
            FROM (SELECT '\"' AS FromString,
                         '"'  AS ToString
                  UNION ALL
                  SELECT '\\',
                         '\'
                  UNION ALL
                  SELECT '\/',
                         '/'
                  UNION ALL
                  SELECT '\b',
                         CHAR(08)
                  UNION ALL
                  SELECT '\f',
                         CHAR(12)
                  UNION ALL
                  SELECT '\n',
                         CHAR(10)
                  UNION ALL
                  SELECT '\r',
                         CHAR(13)
                  UNION ALL
                  SELECT '\t',
                         CHAR(09)) substitutions
            SELECT @result = 0,
                   @escape = 1
            --Begin to take out any hex escape codes
            WHILE @escape > 0
                BEGIN
                    SELECT @index = 0,
                           --find the next hex escape sequence
                           @escape = PATINDEX('%\x[0-9a-f][0-9a-f][0-9a-f][0-9a-f]%',
                                              @token COLLATE SQL_Latin1_General_CP850_Bin)
                    IF @escape > 0 --if there is one
                        BEGIN
                            WHILE @index < 4 --there are always four digits to a \x sequence   
                                BEGIN
                                    SELECT --determine its value
                                           @result = @result + POWER(16,
                                                                     @index)
                                               * (CHARINDEX(SUBSTRING(@token,
                                                                      @escape + 2 + 3
                                                                          - @index, 1),
                                                            @characters) - 1),
                                           @index = @index + 1;

                                END
                            -- and replace the hex sequence by its unicode value
                            SELECT @token = STUFF(@token, @escape, 6,
                                                  NCHAR(@result))
                        END
                END
            --now store the string away 
            INSERT INTO @Strings
                (StringValue)
            SELECT @token
            -- and replace the string with a token
            SELECT @JSON = STUFF(@json, @start, @end + 1,
                                 '@string'
                                     + CONVERT(NVARCHAR(5), @@identity))
        END
    -- all strings are now removed. Now we find the first leaf.  
    WHILE 1 = 1 --forever until there is nothing more to do
        BEGIN

            SELECT @parent_ID = @parent_ID + 1
            --find the first object or list by looking for the open bracket
            SELECT @FirstObject = PATINDEX('%[{[[]%',
                                           @json COLLATE SQL_Latin1_General_CP850_Bin)--object or array
            IF @FirstObject = 0
                BREAK
            IF (SUBSTRING(@json, @FirstObject, 1) = '{')
                SELECT @NextCloseDelimiterChar = '}',
                       @type = 'object'
            ELSE
                SELECT @NextCloseDelimiterChar = ']',
                       @type = 'array'
            SELECT @OpenDelimiter = @firstObject

            WHILE 1 = 1 --find the innermost object or list...
                BEGIN
                    SELECT @lenJSON = LEN(@JSON + '|') - 1
                    --find the matching close-delimiter proceeding after the open-delimiter
                    SELECT @NextCloseDelimiter = CHARINDEX(@NextCloseDelimiterChar,
                                                           @json,
                                                           @OpenDelimiter
                                                               + 1)
                    --is there an intervening open-delimiter of either type
                    SELECT @NextOpenDelimiter = PATINDEX('%[{[[]%',
                                                         RIGHT(@json,
                                                               @lenJSON
                                                                   -
                                                               @OpenDelimiter) COLLATE SQL_Latin1_General_CP850_Bin)--object
                    IF @NextOpenDelimiter = 0
                        BREAK
                    SELECT @NextOpenDelimiter = @NextOpenDelimiter
                        + @OpenDelimiter
                    IF @NextCloseDelimiter < @NextOpenDelimiter
                        BREAK
                    IF SUBSTRING(@json, @NextOpenDelimiter, 1) = '{'
                        SELECT @NextCloseDelimiterChar = '}',
                               @type = 'object'
                    ELSE
                        SELECT @NextCloseDelimiterChar = ']',
                               @type = 'array'
                    SELECT @OpenDelimiter = @NextOpenDelimiter
                END
            ---and parse out the list or name/value pairs
            SELECT @contents = SUBSTRING(@json, @OpenDelimiter + 1,
                                         @NextCloseDelimiter
                                             - @OpenDelimiter - 1)
            SELECT @JSON = STUFF(@json, @OpenDelimiter,
                                 @NextCloseDelimiter - @OpenDelimiter + 1,
                                 '@' + @type
                                     + CONVERT(NVARCHAR(5), @parent_ID))
            WHILE (PATINDEX('%[A-Za-z0-9@+.e]%',
                            @contents COLLATE SQL_Latin1_General_CP850_Bin)) <> 0
                BEGIN
                    IF @Type = 'Object' --it will be a 0-n list containing a string followed by a string, number,boolean, or null
                        BEGIN
                            SELECT @SequenceNo = 0,
                                   @end = CHARINDEX(':', ' ' + @contents)--if there is anything, it will be a string-based name.
                            SELECT @start = PATINDEX('%[^A-Za-z@][@]%',
                                                     ' ' + @contents COLLATE SQL_Latin1_General_CP850_Bin)--AAAAAAAA
                            SELECT @token = SUBSTRING(' ' + @contents,
                                                      @start + 1,
                                                      @End - @Start - 1),
                                   @endofname = PATINDEX('%[0-9]%',
                                                         @token COLLATE SQL_Latin1_General_CP850_Bin),
                                   @param = RIGHT(@token,
                                                  LEN(@token)
                                                      - @endofname + 1)
                            SELECT @token = LEFT(@token, @endofname - 1),
                                   @Contents = RIGHT(' ' + @contents,
                                                     LEN(' ' + @contents
                                                         + '|') - @end
                                                         - 1)
                            SELECT @name = stringvalue
                            FROM @strings
                            WHERE string_id = @param --fetch the name
                        END
                    ELSE
                        SELECT @Name = NULL,
                               @SequenceNo = @SequenceNo + 1
                    SELECT @end = CHARINDEX(',', @contents)-- a string-token, object-token, list-token, number,boolean, or null
                    IF @end = 0
                        BEGIN
                            SELECT @end = PATINDEX('%[A-Za-z0-9@+.e][^A-Za-z0-9@+.e]%',
                                                   @Contents + ' ' COLLATE SQL_Latin1_General_CP850_Bin)
                                + 1
                        END
                    SELECT @start = PATINDEX('%[^-A-Za-z0-9@+.e][-A-Za-z0-9@+.e]%',
                                             ' ' + @contents COLLATE SQL_Latin1_General_CP850_Bin)
                    --select @start,@end, LEN(@contents+'|'), @contents  
                    SELECT @Value = RTRIM(SUBSTRING(@contents, @start,
                                                    @End - @Start)),
                           @Contents = RIGHT(@contents + ' ',
                                             LEN(@contents + '|') - @end)
                    IF SUBSTRING(@value, 1, 7) = '@object'
                        INSERT INTO @hierarchy
                        (NAME,
                         SequenceNo,
                         parent_ID,
                         StringValue,
                         Object_ID,
                         ValueType)
                        SELECT @name,
                               @SequenceNo,
                               @parent_ID,
                               SUBSTRING(@value, 8, 5),
                               SUBSTRING(@value, 8, 5),
                               'object'
                    ELSE
                        IF SUBSTRING(@value, 1, 6) = '@array'
                            INSERT INTO @hierarchy
                            (NAME,
                             SequenceNo,
                             parent_ID,
                             StringValue,
                             Object_ID,
                             ValueType)
                            SELECT @name,
                                   @SequenceNo,
                                   @parent_ID,
                                   SUBSTRING(@value, 7, 5),
                                   SUBSTRING(@value, 7, 5),
                                   'array'
                        ELSE
                            IF SUBSTRING(@value, 1, 7) = '@string'
                                INSERT INTO @hierarchy
                                (NAME,
                                 SequenceNo,
                                 parent_ID,
                                 StringValue,
                                 ValueType)
                                SELECT @name,
                                       @SequenceNo,
                                       @parent_ID,
                                       stringvalue,
                                       'string'
                                FROM @strings
                                WHERE string_id = SUBSTRING(@value,
                                                            8, 5)
                            ELSE
                                IF @value IN ('true', 'false')
                                    INSERT INTO @hierarchy
                                    (NAME,
                                     SequenceNo,
                                     parent_ID,
                                     StringValue,
                                     ValueType)
                                    SELECT @name,
                                           @SequenceNo,
                                           @parent_ID,
                                           @value,
                                           'boolean'
                                ELSE
                                    IF @value = 'null'
                                        INSERT INTO @hierarchy
                                        (NAME,
                                         SequenceNo,
                                         parent_ID,
                                         StringValue,
                                         ValueType)
                                        SELECT @name,
                                               @SequenceNo,
                                               @parent_ID,
                                               @value,
                                               'null'
                                    ELSE
                                        IF PATINDEX('%[^0-9]%',
                                                    @value COLLATE SQL_Latin1_General_CP850_Bin) > 0
                                            INSERT INTO @hierarchy
                                            (NAME,
                                             SequenceNo,
                                             parent_ID,
                                             StringValue,
                                             ValueType)
                                            SELECT @name,
                                                   @SequenceNo,
                                                   @parent_ID,
                                                   @value,
                                                   'real'
                                        ELSE
                                            INSERT INTO @hierarchy
                                            (NAME,
                                             SequenceNo,
                                             parent_ID,
                                             StringValue,
                                             ValueType)
                                            SELECT @name,
                                                   @SequenceNo,
                                                   @parent_ID,
                                                   @value,
                                                   'int'
                    IF @Contents = ' '
                        SELECT @SequenceNo = 0
                END
        END
    INSERT INTO @hierarchy
    (NAME,
     SequenceNo,
     parent_ID,
     StringValue,
     Object_ID,
     ValueType)
    SELECT '-',
           1,
           NULL,
           '',
           @parent_id - 1,
           @type
--
    RETURN
END

引用
[1]: 部分代码来自如下地址 :Sqlserver2008解析json字符串新增到临时表中

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
GAN(Generative Adversarial Networks)是一种非常流行的生成式深度学习模型,用于生成一些看起来非常逼真的图像。如果你想用GAN来生成二次元妹子,你可以按照以下步骤来设计你的GAN模型,并训练和可视化它。 1. 设计GAN模型 GAN模型由两个神经网络组成:生成器和判别器。生成器的目标是生成与真实图像相似的假图像,而判别器的目标是将真实图像与生成的假图像区分开来。GAN模型的训练过程是两个神经网络相互博弈的过程。 你可以设计生成器和判别器的结构,但是一般情况下,你可以使用卷积神经网络(Convolutional Neural Networks)来实现它们。生成器将一个随机噪声向量转换为一张图像,而判别器接受一张图像并输出一个二元值,表示这张图像是真实的还是假的。 2. 定义损失函数 GAN模型的损失函数由两个部分组成:生成器的损失和判别器的损失。生成器的损失是生成的假图像与真实图像之间的差异,而判别器的损失是真实图像和生成的假图像之间的差异。 你可以使用二元交叉熵损失函数来定义判别器的损失,因为GAN模型是一个二元分类问题。对于生成器的损失,你可以使用L1或L2损失函数,因为它们可以度量生成的假图像与真实图像之间的差异。 3. 训练GAN模型 你可以使用真实图像和随机噪声向量来训练GAN模型。在每次训练,你需要先训练判别器,然后训练生成器。 在训练判别器时,你需要将真实图像标记为1,将生成的假图像标记为0,并计算判别器损失。在训练生成器时,你需要生成一个随机噪声向量,并将其输入到生成器,然后计算生成器损失。 4. 可视化GAN模型 你可以使用TensorBoard等工具来可视化GAN模型的训练过程。你可以绘制判别器和生成器的损失函数随时间的变化,以及生成的假图像与真实图像之间的差异。这将帮助你了解GAN模型的训练过程,并调整模型的超参数。 下面是一个例子代码,用于训练一个GAN模型,生成二次元妹子。 ```python import tensorflow as tf from tensorflow.keras import layers import numpy as np import matplotlib.pyplot as plt # 定义生成器 def make_generator_model(): model = tf.keras.Sequential() model.add(layers.Dense(256, use_bias=False, input_shape=(100,))) model.add(layers.BatchNormalization()) model.add(layers.LeakyReLU()) model.add(layers.Dense(512, use_bias=False)) model.add(layers.BatchNormalization()) model.add(layers.LeakyReLU()) model.add(layers.Dense(1024, use_bias=False)) model.add(layers.BatchNormalization()) model.add(layers.LeakyReLU()) model.add(layers.Dense(28*28*3, use_bias=False, activation='tanh')) model.add(layers.Reshape((28, 28, 3))) return model # 定义判别器 def make_discriminator_model(): model = tf.keras.Sequential() model.add(layers.Flatten()) model.add(layers.Dense(512)) model.add(layers.LeakyReLU()) model.add(layers.Dense(256)) model.add(layers.LeakyReLU()) model.add(layers.Dense(1)) return model # 定义损失函数 cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True) def discriminator_loss(real_output, fake_output): real_loss = cross_entropy(tf.ones_like(real_output), real_output) fake_loss = cross_entropy(tf.zeros_like(fake_output), fake_output) total_loss = real_loss + fake_loss return total_loss def generator_loss(fake_output): return cross_entropy(tf.ones_like(fake_output), fake_output) # 定义优化器 generator_optimizer = tf.keras.optimizers.Adam(1e-4) discriminator_optimizer = tf.keras.optimizers.Adam(1e-4) # 定义训练函数 @tf.function def train_step(images): noise = tf.random.normal([BATCH_SIZE, 100]) with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape: generated_images = generator(noise, training=True) real_output = discriminator(images, training=True) fake_output = discriminator(generated_images, training=True) gen_loss = generator_loss(fake_output) disc_loss = discriminator_loss(real_output, fake_output) gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables) gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables) generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables)) discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables)) # 加载数据 (train_images, _), (_, _) = tf.keras.datasets.mnist.load_data() train_images = train_images.reshape(train_images.shape[0], 28, 28, 1).astype('float32') train_images = (train_images - 127.5) / 127.5 # 将像素值标准化为[-1, 1] BUFFER_SIZE = 60000 BATCH_SIZE = 256 train_dataset = tf.data.Dataset.from_tensor_slices(train_images).shuffle(BUFFER_SIZE).batch(BATCH_SIZE) # 训练模型 EPOCHS = 100 noise_dim = 100 num_examples_to_generate = 16 generator = make_generator_model() discriminator = make_discriminator_model() for epoch in range(EPOCHS): for image_batch in train_dataset: train_step(image_batch) # 绘制生成的图像 if epoch % 10 == 0: noise = tf.random.normal([num_examples_to_generate, noise_dim]) generated_images = generator(noise, training=False) fig, axs = plt.subplots(4, 4) cnt = 0 for i in range(4): for j in range(4): axs[i,j].imshow(generated_images[cnt, :, :, :]) axs[i,j].axis('off') cnt += 1 plt.show() ``` 在这个例子,我们使用了MNIST数据的手写数字图像来训练GAN模型。在训练过程,我们每10个epoch绘制一次生成的二次元妹子图像,以观察生成器的训练效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值