jackson学习之三:常用API操作

java服务端 专栏收录该内容
129 篇文章 12 订阅

欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos

系列文章汇总

源码下载

  1. 如果您不想编码,可以在GitHub下载所有源码,地址和链接信息如下表所示(https://github.com/zq2599/blog_demos):
名称链接备注
项目主页https://github.com/zq2599/blog_demos该项目在GitHub上的主页
git仓库地址(https)https://github.com/zq2599/blog_demos.git该项目源码的仓库地址,https协议
git仓库地址(ssh)git@github.com:zq2599/blog_demos.git该项目源码的仓库地址,ssh协议
  1. 这个git项目中有多个文件夹,本章的应用在jacksondemo文件夹下,如下图红框所示:
    在这里插入图片描述

本篇概览

本文是《jackson学习》系列的第三篇,前面咱们学习了jackson的低阶API,知道了底层原理,本篇开始学习平时最常用的基本功能,涉及内容如下:

  1. 体验最常用的操作,内容如下图所示:
    在这里插入图片描述

  2. 介绍常用的可配置属性,以便按需要来设置;

  3. 接下来进入快速浏览的环节,咱们一起先把各个API过一遍;

单个对象序列化

先看常用的序列化API:

  1. 对象转字符串:
String jsonStr = mapper.writeValueAsString(twitterEntry);
  1. 对象转文件:
mapper.writeValue(new File("twitter.json"), twitterEntry);
  1. 对象转byte数组:
byte[] array = mapper.writeValueAsBytes(twitterEntry);

单个对象反序列化

  1. 字符串转对象:
TwitterEntry tFromStr = mapper.readValue(objectJsonStr, TwitterEntry.class);
  1. 文件转对象:
TwitterEntry tFromFile = mapper.readValue(new File("twitter.json"), TwitterEntry.class);
  1. byte数组转对象:
TwitterEntry tFromBytes = mapper.readValue(array, TwitterEntry.class);
  1. 字符串网络地址转对象:
String testJsonDataUrl = "https://raw.githubusercontent.com/zq2599/blog_demos/master/files/twitteer_message.json";

TwitterEntry tFromUrl = mapper.readValue(new URL(testJsonDataUrl), TwitterEntry.class);

集合序列化

  1. HashMap转字符串:
String mapJsonStr = mapper.writeValueAsString(map);

集合反序列化

  1. 字符串转HashMap:
Map<String, Object> mapFromStr = mapper.readValue(mapJsonStr, new TypeReference<Map<String, Object>>() {});

JsonNode

  1. 如果您不想使用XXX.class来做反序列化,也能使用JsonNode来操作:
JsonNode jsonNode = mapper.readTree(mapJsonStr);
String name = jsonNode.get("name").asText();
int age = jsonNode.get("age").asInt();
String city = jsonNode.get("addr").get("city").asText();
String street = jsonNode.get("addr").get("street").asText();

时间字段格式化

  1. 对于Date字段,默认的反序列化是时间戳,可以修改配置:
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"));
dateMapStr = mapper.writeValueAsString(dateMap);

JSON数组的反序列化

假设jsonArrayStr是个json数组格式的字符串:

  1. JSON数组转对象数组:
TwitterEntry[] twitterEntryArray = mapper.readValue(jsonArrayStr, TwitterEntry[].class);
  1. JSON数组转对象集合(ArrayList):
List<TwitterEntry> twitterEntryList = mapper.readValue(jsonArrayStr, new TypeReference<List<TwitterEntry>>() {});

完整代码

  1. 上述所有常用API用法的完整代码如下:
package com.bolingcavalry.jacksondemo.databind;

import com.bolingcavalry.jacksondemo.beans.TwitterEntry;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.*;

public class SimpleDemo {

    private static final Logger logger = LoggerFactory.getLogger(SimpleDemo.class);

    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();

        logger.info("以下是序列化操作");

        // 对象 -> 字符串
        TwitterEntry twitterEntry = new TwitterEntry();
        twitterEntry.setId(123456L);
        twitterEntry.setFromUserId(101);
        twitterEntry.setToUserId(102);
        twitterEntry.setText("this is a message for serializer test");
        twitterEntry.setLanguageCode("zh");

        String jsonStr = mapper.writeValueAsString(twitterEntry);
        logger.info("序列化的字符串:{}", jsonStr);

        // 对象 -> 文件
        mapper.writeValue(new File("twitter.json"), twitterEntry);

        // 对象 -> byte数组
        byte[] array = mapper.writeValueAsBytes(twitterEntry);

        logger.info("\n\n以下是反序列化操作");

        // 字符串 -> 对象
        String objectJsonStr = "{\n" +
                "  \"id\":1125687077,\n" +
                "  \"text\":\"@stroughtonsmith You need to add a \\\"Favourites\\\" tab to TC/iPhone. Like what TwitterFon did. I can't WAIT for your Twitter App!! :) Any ETA?\",\n" +
                "  \"fromUserId\":855523, \n" +
                "  \"toUserId\":815309,\n" +
                "  \"languageCode\":\"en\"\n" +
                "}";


        TwitterEntry tFromStr = mapper.readValue(objectJsonStr, TwitterEntry.class);
        logger.info("从字符串反序列化的对象:{}", tFromStr);

        // 文件 -> 对象
        TwitterEntry tFromFile = mapper.readValue(new File("twitter.json"), TwitterEntry.class);
        logger.info("从文件反序列化的对象:{}", tFromStr);

        // byte数组 -> 对象
        TwitterEntry tFromBytes = mapper.readValue(array, TwitterEntry.class);
        logger.info("从byte数组反序列化的对象:{}", tFromBytes);

        // 字符串网络地址 -> 对象
        String testJsonDataUrl = "https://raw.githubusercontent.com/zq2599/blog_demos/master/files/twitteer_message.json";

        TwitterEntry tFromUrl = mapper.readValue(new URL(testJsonDataUrl), TwitterEntry.class);
        logger.info("从网络地址反序列化的对象:{}", tFromUrl);


        logger.info("\n\n以下是集合序列化操作");

        Map<String, Object> map = new HashMap<>();
        map.put("name", "tom");
        map.put("age", 11);

        Map<String, String> addr = new HashMap<>();
        addr.put("city","深圳");
        addr.put("street", "粤海");

        map.put("addr", addr);

        String mapJsonStr = mapper.writeValueAsString(map);
        logger.info("HashMap序列化的字符串:{}", mapJsonStr);

        logger.info("\n\n以下是集合反序列化操作");
        Map<String, Object> mapFromStr = mapper.readValue(mapJsonStr, new TypeReference<Map<String, Object>>() {});
        logger.info("从字符串反序列化的HashMap对象:{}", mapFromStr);

        // JsonNode类型操作
        JsonNode jsonNode = mapper.readTree(mapJsonStr);
        String name = jsonNode.get("name").asText();
        int age = jsonNode.get("age").asInt();
        String city = jsonNode.get("addr").get("city").asText();
        String street = jsonNode.get("addr").get("street").asText();

        logger.info("用JsonNode对象和API反序列化得到的数:name[{}]、age[{}]、city[{}]、street[{}]", name, age, city, street);

        // 时间类型格式

        Map<String, Object> dateMap = new HashMap<>();
        dateMap.put("today", new Date());

        String dateMapStr = mapper.writeValueAsString(dateMap);
        logger.info("默认的时间序列化:{}", dateMapStr);

        // 设置时间格式
        mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"));
        dateMapStr = mapper.writeValueAsString(dateMap);
        logger.info("自定义的时间序列化:{}", dateMapStr);

        System.out.println(objectJsonStr);

        // json数组
        String jsonArrayStr = "[{\n" +
                "  \"id\":1,\n" +
                "  \"text\":\"text1\",\n" +
                "  \"fromUserId\":11, \n" +
                "  \"toUserId\":111,\n" +
                "  \"languageCode\":\"en\"\n" +
                "},\n" +
                "{\n" +
                "  \"id\":2,\n" +
                "  \"text\":\"text2\",\n" +
                "  \"fromUserId\":22, \n" +
                "  \"toUserId\":222,\n" +
                "  \"languageCode\":\"zh\"\n" +
                "},\n" +
                "{\n" +
                "  \"id\":3,\n" +
                "  \"text\":\"text3\",\n" +
                "  \"fromUserId\":33, \n" +
                "  \"toUserId\":333,\n" +
                "  \"languageCode\":\"en\"\n" +
                "}]";

        // json数组 -> 对象数组
        TwitterEntry[] twitterEntryArray = mapper.readValue(jsonArrayStr, TwitterEntry[].class);
        logger.info("json数组反序列化成对象数组:{}", Arrays.toString(twitterEntryArray));

        // json数组 -> 对象集合
        List<TwitterEntry> twitterEntryList = mapper.readValue(jsonArrayStr, new TypeReference<List<TwitterEntry>>() {});
        logger.info("json数组反序列化成对象集合:{}", twitterEntryList);
    }
}
  1. 执行结果如下:
C:\jdk\bin\java.exe -javaagent:C:\sofware\JetBrains\IntelliJIDEA\lib\idea_rt.jar=64570:C:\sofware\JetBrains\IntelliJIDEA\bin -Dfile.encoding=UTF-8 -classpath C:\jdk\jre\lib\charsets.jar;C:\jdk\jre\lib\deploy.jar;C:\jdk\jre\lib\ext\access-bridge-64.jar;C:\jdk\jre\lib\ext\cldrdata.jar;C:\jdk\jre\lib\ext\dnsns.jar;C:\jdk\jre\lib\ext\jaccess.jar;C:\jdk\jre\lib\ext\jfxrt.jar;C:\jdk\jre\lib\ext\localedata.jar;C:\jdk\jre\lib\ext\nashorn.jar;C:\jdk\jre\lib\ext\sunec.jar;C:\jdk\jre\lib\ext\sunjce_provider.jar;C:\jdk\jre\lib\ext\sunmscapi.jar;C:\jdk\jre\lib\ext\sunpkcs11.jar;C:\jdk\jre\lib\ext\zipfs.jar;C:\jdk\jre\lib\javaws.jar;C:\jdk\jre\lib\jce.jar;C:\jdk\jre\lib\jfr.jar;C:\jdk\jre\lib\jfxswt.jar;C:\jdk\jre\lib\jsse.jar;C:\jdk\jre\lib\management-agent.jar;C:\jdk\jre\lib\plugin.jar;C:\jdk\jre\lib\resources.jar;C:\jdk\jre\lib\rt.jar;D:\github\blog_demos\jacksondemo\databind\target\classes;C:\Users\12167\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.11.0\jackson-databind-2.11.0.jar;C:\Users\12167\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.11.0\jackson-annotations-2.11.0.jar;C:\Users\12167\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.11.0\jackson-core-2.11.0.jar;C:\Users\12167\.m2\repository\org\slf4j\slf4j-log4j12\1.7.25\slf4j-log4j12-1.7.25.jar;C:\Users\12167\.m2\repository\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar;C:\Users\12167\.m2\repository\log4j\log4j\1.2.17\log4j-1.2.17.jar;C:\Users\12167\.m2\repository\commons-io\commons-io\2.7\commons-io-2.7.jar;C:\Users\12167\.m2\repository\org\apache\commons\commons-lang3\3.10\commons-lang3-3.10.jar;D:\github\blog_demos\jacksondemo\beans\target\classes com.bolingcavalry.jacksondemo.databind.SimpleDemo
2020-08-28 07:53:01 INFO  SimpleDemo:27 - 以下是序列化操作
2020-08-28 07:53:01 INFO  SimpleDemo:38 - 序列化的字符串:{"id":123456,"text":"this is a message for serializer test","fromUserId":101,"toUserId":102,"languageCode":"zh"}
2020-08-28 07:53:01 INFO  SimpleDemo:47 - 

以下是反序列化操作
2020-08-28 07:53:01 INFO  SimpleDemo:60 - 从字符串反序列化的对象:[Tweet, id: 1125687077, text='@stroughtonsmith You need to add a "Favourites" tab to TC/iPhone. Like what TwitterFon did. I can't WAIT for your Twitter App!! :) Any ETA?', from: 855523, to: 815309, lang: en]
2020-08-28 07:53:01 INFO  SimpleDemo:64 - 从文件反序列化的对象:[Tweet, id: 1125687077, text='@stroughtonsmith You need to add a "Favourites" tab to TC/iPhone. Like what TwitterFon did. I can't WAIT for your Twitter App!! :) Any ETA?', from: 855523, to: 815309, lang: en]
2020-08-28 07:53:01 INFO  SimpleDemo:68 - 从byte数组反序列化的对象:[Tweet, id: 123456, text='this is a message for serializer test', from: 101, to: 102, lang: zh]
2020-08-28 07:53:04 INFO  SimpleDemo:74 - 从网络地址反序列化的对象:[Tweet, id: 112233445566, text='this is a message from zq2599's github', from: 201, to: 202, lang: en]
2020-08-28 07:53:04 INFO  SimpleDemo:77 - 

以下是集合序列化操作
2020-08-28 07:53:04 INFO  SimpleDemo:90 - HashMap序列化的字符串:{"name":"tom","addr":{"city":"深圳","street":"粤海"},"age":11}
2020-08-28 07:53:04 INFO  SimpleDemo:92 - 

以下是集合反序列化操作
2020-08-28 07:53:04 INFO  SimpleDemo:94 - 从字符串反序列化的HashMap对象:{name=tom, addr={city=深圳, street=粤海}, age=11}
2020-08-28 07:53:04 INFO  SimpleDemo:103 - 用JsonNode对象和API反序列化得到的数:name[tom]、age[11]、city[深圳]、street[粤海]
2020-08-28 07:53:04 INFO  SimpleDemo:111 - 默认的时间序列化:{"today":1598572384838}
2020-08-28 07:53:04 INFO  SimpleDemo:116 - 自定义的时间序列化:{"today":"2020-08-28 07:53:04"}
{
  "id":1125687077,
  "text":"@stroughtonsmith You need to add a \"Favourites\" tab to TC/iPhone. Like what TwitterFon did. I can't WAIT for your Twitter App!! :) Any ETA?",
  "fromUserId":855523, 
  "toUserId":815309,
  "languageCode":"en"
}
2020-08-28 07:53:04 INFO  SimpleDemo:145 - json数组反序列化成对象数组:[[Tweet, id: 1, text='text1', from: 11, to: 111, lang: en], [Tweet, id: 2, text='text2', from: 22, to: 222, lang: zh], [Tweet, id: 3, text='text3', from: 33, to: 333, lang: en]]
2020-08-28 07:53:04 INFO  SimpleDemo:149 - json数组反序列化成对象集合:[[Tweet, id: 1, text='text1', from: 11, to: 111, lang: en], [Tweet, id: 2, text='text2', from: 22, to: 222, lang: zh], [Tweet, id: 3, text='text3', from: 33, to: 333, lang: en]]

Process finished with exit code 0
  1. 还会产生名为twitter.json的文件,内容如下:
{"id":123456,"text":"this is a message for serializer test","fromUserId":101,"toUserId":102,"languageCode":"zh"}

常用配置

下面是平时可能用到的自定义配置项目:

  1. 序列化结果格式化:
mapper.enable(SerializationFeature.INDENT_OUTPUT);
  1. 空对象不要抛出异常:
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
  1. Date、Calendar等序列化为时间格式的字符串(如果不执行以下设置,就会序列化成时间戳格式):
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
  1. 反序列化时,遇到未知属性不要抛出异常:
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
  1. 反序列化时,空字符串对于的实例属性为null:
mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
  1. 允许C和C++样式注释:
mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
  1. 允许字段名没有引号(可以进一步减小json体积):
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
  1. 允许单引号:
mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);

特殊配置:在json对象最外层再包裹一层

  1. 最后要说的是个特殊配置,先来看看正常情况一个普通的序列化结果:
{
  "id" : 1,
  "text" : "aabbcc",
  "fromUserId" : 456,
  "toUserId" : 0,
  "languageCode" : "zh"
}
  1. 接下来咱们做两件事,首先,是给上述json对应的实例类添加一个注解,如下图红框:
    在这里插入图片描述
  2. 其次,执行以下配置:
mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
  1. 然后再次执行TwitterEntry实例的序列化,得到的结果如下,可见和之前的序列化结果相比,之前的整个json都变成了一个value,此value对应的key就是注解JsonRootName的value属性:
{
  "aaa" : {
    "id" : 1,
    "text" : "aabbcc",
    "fromUserId" : 456,
    "toUserId" : 0,
    "languageCode" : "zh"
  }
}
  • 至此,开发中常用的API和配置都已经介绍完成,希望能给您带来一些参考,接下来的章节,咱们一起去了解另一个常用操作:jackson注解

欢迎关注公众号:程序员欣宸

微信搜索「程序员欣宸」,我是欣宸,期待与您一同畅游Java世界…

  • 9
    点赞
  • 4
    评论
  • 20
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

======================= jackson 帮助文档 ======================= 所有类 Annotated AnnotatedClass AnnotatedClass.FactoryMethodFilter AnnotatedConstructor AnnotatedMethod AnnotatedMethodMap AnnotationFilter AnnotationMap ArrayBuilders ArrayBuilders.BooleanBuilder ArrayBuilders.ByteBuilder ArrayBuilders.DoubleBuilder ArrayBuilders.FloatBuilder ArrayBuilders.IntBuilder ArrayBuilders.LongBuilder ArrayBuilders.ShortBuilder ArrayDeserializer ArrayDeserializers ArrayNode ArraySerializers ArraySerializers.BooleanArraySerializer ArraySerializers.ByteArraySerializer ArraySerializers.CharArraySerializer ArraySerializers.DoubleArraySerializer ArraySerializers.FloatArraySerializer ArraySerializers.IntArraySerializer ArraySerializers.LongArraySerializer ArraySerializers.ObjectArraySerializer ArraySerializers.ShortArraySerializer ArraySerializers.StringArraySerializer ArrayType Base64Variant Base64Variants BaseJsonNode BasicBeanDescription BasicClassIntrospector BasicClassIntrospector.GetterMethodFilter BasicClassIntrospector.SetterAndGetterMethodFilter BasicClassIntrospector.SetterMethodFilter BasicDeserializerFactory BasicSerializerFactory BasicSerializerFactory.BooleanSerializer BasicSerializerFactory.CalendarSerializer BasicSerializerFactory.ClassSerializer BasicSerializerFactory.DoubleSerializer BasicSerializerFactory.EnumSerializer BasicSerializerFactory.FloatSerializer BasicSerializerFactory.IntegerSerializer BasicSerializerFactory.IntLikeSerializer BasicSerializerFactory.LongSerializer BasicSerializerFactory.NullSerializer BasicSerializerFactory.NumberSerializer BasicSerializerFactory.SerializableSerializer BasicSerializerFactory.SqlDateSerializer BasicSerializerFactory.SqlTimeSerializer BasicSerializerFactory.StringLikeSerializer BasicSerializerFactory.StringSerializer BasicSerializerFactory.UtilDateSerializer BeanDescription BeanDeserializer BeanDeserializerFactory BeanPropertyWriter BeanSerializer BeanSerializerFactory BigIntegerNode BinaryNode BooleanNode BufferRecycler BufferRecycler.ByteBufferType BufferRecycler.CharBufferType ByteArrayBuilder ByteSourceBootstrapper BytesToNameCanonicalizer CharsToNameCanonicalizer CharTypes ClassIntrospector ClassKey ClassUtil CollectionDeserializer CollectionType ContainerNode ContainerSerializers ContainerSerializers.CollectionSerializer ContainerSerializers.EnumMapSerializer ContainerSerializers.EnumSetSerializer ContainerSerializers.IndexedListSerializer ContainerSerializers.IterableSerializer ContainerSerializers.IteratorSerializer ContainerSerializers.MapSerializer CustomDeserializerFactory CustomSerializerFactory DateDeserializer DecimalNode DefaultPrettyPrinter DefaultPrettyPrinter.FixedSpaceIndenter DefaultPrettyPrinter.Lf2SpacesIndenter DefaultPrettyPrinter.NopIndenter DeserializationConfig DeserializationConfig.Feature DeserializationContext DeserializationProblemHandler DeserializerFactory DeserializerProvider DoubleNode EnumDeserializer EnumMapDeserializer EnumResolver EnumSetDeserializer FailingSerializer FromStringDeserializer FromStringDeserializer.CurrencyDeserializer FromStringDeserializer.URIDeserializer FromStringDeserializer.URLDeserializer FromStringDeserializer.UUIDDeserializer Indenter InternCache IntNode IOContext JacksonAnnotationFilter JavaType JsonAnySetter JsonAutoDetect JsonClass JsonContentClass JsonCreator JsonDeserializer JsonEncoding JsonFactory JsonGenerationException JsonGenerator JsonGenerator.Feature JsonGeneratorBase JsonGetter JsonIgnore JsonKeyClass JsonLocation JsonMappingException JsonMappingException.Reference JsonMethod JsonNode JsonNodeDeserializer JsonNodeFactory JsonNumericParserBase JsonParseException JsonParser JsonParser.Feature JsonParser.NumberType JsonParserBase JsonProcessingException JsonReadContext JsonSerializable JsonSerializer JsonSetter JsonStreamContext JsonToken JsonUseDeserializer JsonUseSerializer JsonValue JsonValueSerializer JsonWriteContext JsonWriteNullProperties KeyDeserializer LinkedNode LongNode MapDeserializer MappingJsonFactory MapType MergedStream MethodFilter MethodKey MissingNode Name Name1 Name2 Name3 NameN NoClass NullNode NumberInput NumberOutput NumericNode ObjectBuffer ObjectCodec ObjectMapper ObjectNode POJONode PrettyPrinter PrimitiveArrayBuilder ReaderBasedNumericParser ReaderBasedParser ReaderBasedParserBase ReadOnlyClassToSerializerMap ResolvableDeserializer ResolvableSerializer SerializationConfig SerializationConfig.Feature SerializerCache SerializerFactory SerializerProvider SettableAnyProperty SettableBeanProperty SimpleType StdDateFormat StdDeserializationContext StdDeserializer StdDeserializer.BigDecimalDeserializer StdDeserializer.BigIntegerDeserializer StdDeserializer.BooleanDeserializer StdDeserializer.ByteDeserializer StdDeserializer.CalendarDeserializer StdDeserializer.CharacterDeserializer StdDeserializer.ClassDeserializer StdDeserializer.DoubleDeserializer StdDeserializer.FloatDeserializer StdDeserializer.IntegerDeserializer StdDeserializer.LongDeserializer StdDeserializer.NumberDeserializer StdDeserializer.ShortDeserializer StdDeserializer.SqlDateDeserializer StdDeserializer.StackTraceElementDeserializer StdDeserializer.StringDeserializer StdDeserializerProvider StdKeyDeserializer StdKeySerializer StdSerializerProvider StreamBasedParserBase TextBuffer TextNode ThrowableDeserializer ToStringSerializer TreeMapper TreeMapper.DupFields TypeFactory TypeReference TypeReference UntypedObjectDeserializer UTF32Reader Utf8NumericParser Utf8StreamParser UTF8Writer ValueNode WriterBasedGenerator
©️2021 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值