`
rensanning
  • 浏览: 3545885 次
  • 性别: Icon_minigender_1
  • 来自: 大连
博客专栏
Efef1dba-f7dd-3931-8a61-8e1c76c3e39f
使用Titanium Mo...
浏览量:38094
Bbab2146-6e1d-3c50-acd6-c8bae29e307d
Cordova 3.x入门...
浏览量:607083
C08766e7-8a33-3f9b-9155-654af05c3484
常用Java开源Libra...
浏览量:682027
77063fb3-0ee7-3bfa-9c72-2a0234ebf83e
搭建 CentOS 6 服...
浏览量:89199
E40e5e76-1f3b-398e-b6a6-dc9cfbb38156
Spring Boot 入...
浏览量:401675
Abe39461-b089-344f-99fa-cdfbddea0e18
基于Spring Secu...
浏览量:69651
66a41a70-fdf0-3dc9-aa31-19b7e8b24672
MQTT入门
浏览量:91646
社区版块
存档分类
最新评论

JAXB实例入门

 
阅读更多
JAXB(Java Architecture for XML Binding),JDK标准规范,Java对象和XML之间的转换,和SAX/DOM不同的是无需关注XML解析细节。
  • Marshalling – 把Java对象转换成XML
  • Unmarshalling – 把XML转换成Java对象
版本:
* JDK1.6开始提供JAXB 2.0(建议使用最新版JDK)
* 低于JDK1.6的版本需要下载jaxb-api.jar和jaxb-impl.jar
* JAXB还有JDK以外的很多第三方实现,比如:EclipseLink MOXy

以下代码都去掉了Bean的Setter/Getter,具体可执行的完整工程代码,可从附件下载。

(1) 基本

Java对象 -> XML
@XmlRootElement
public class Sample {
	private int id;
	private String name;
	private boolean flag;
}

		Sample s = new Sample();
		s.setId(101);
		s.setName("basicObj2XML");
		s.setFlag(false);
		
		StringWriter w = new StringWriter();
		// JAXB是JDK封装好的解析器
		JAXB.marshal(s, new StreamResult(w)); // 输出到字符串
		System.out.println(w.toString());

引用
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sample>
    <flag>false</flag>
    <id>101</id>
    <name>basicObj2XML</name>
</sample>


转换后的XML内容可以直接输出到其他流(比如:控制台、文件等)
		Sample s = new Sample();
		s.setId(102);
		s.setName("basicObj2Output");
		s.setFlag(false);

		JAXB.marshal(s, System.out); // 输出到控制台
		JAXB.marshal(s, new File("d:\\jaxb_sample.xml")); // 输出到文件


XML -> Java对象
		String xml = "<?xml version=\"1.0\"?>" 
					+ "<sample>" 
					+ "  <id>103</id>" 
					+ "  <name>basicXML2Obj</name>" 
					+ "  <flag>false</flag>" 
					+ "</sample>";
		
		StringReader r = new StringReader(xml);
		Sample ss = JAXB.unmarshal(r, Sample.class);
		System.out.println(ss.toString());


(2) 通过JAXBContext创建解析器

创建Marshaller
		JAXBContext jaxbContext = JAXBContext.newInstance(Sample.class);
		Marshaller marshaller = jaxbContext.createMarshaller();
		marshaller.marshal(s, System.out);


创建Unmarshaller
		JAXBContext jaxbContext = JAXBContext.newInstance(Sample.class);
		Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
		StringReader r = new StringReader(xml);
		Sample sample = unmarshaller.unmarshal(new StreamSource(r), Sample.class).getValue();
		System.out.println(sample.toString());


设置Marshaller属性
marshaller.setProperty();
		JAXBContext jaxbContext = JAXBContext.newInstance(Sample.class);
		Marshaller marshaller = jaxbContext.createMarshaller();
		// 设置格式化后输出
		marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
		// 设置字符集
		marshaller.setProperty(Marshaller.JAXB_ENCODING, Charset.defaultCharset().name());
		marshaller.marshal(s, System.out);


(3) 采用EclipseLink MOXy作为JAXB provider
1)下载导入eclipselink.jar
2)在POJO的包下新建jaxb.properties
引用
#Sun JAXB
javax.xml.bind.context.factory=com.sun.xml.internal.bind.v2.ContextFactory
#Eclipse MOXy
#javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

*** SUN的JAXB实现会在xml中添加属性standalone="yes",而EclipseLink MOXy就不会。

(4) 监听器

marshaller.setListener();

Marshaller监听器
public class MarshallListener extends Marshaller.Listener {

	public void beforeMarshal(Object source) {
		System.out.println("BEFORE_MARSHAL fired");
	}

	public void afterMarshal(Object source) {
		System.out.println("AFTER_MARSHAL fired");
	}

}

		JAXBContext jaxbContext = JAXBContext.newInstance(Sample.class);
		Marshaller marshaller = jaxbContext.createMarshaller();
		// 设置监听器(!!!Sun JAXB实现有Bug会被执行两次,而Eclipse MOXy能正确执行一次!!!)
		marshaller.setListener(new MarshallListener());
		marshaller.marshal(s, System.out);


Unmarshaller监听器
public class UnmarshallListener extends Unmarshaller.Listener {

	@Override
	public void beforeUnmarshal(Object target, Object parent) {
		System.out.println("BEFORE_UNMARSHAL fired");
	}

	@Override
	public void afterUnmarshal(Object target, Object parent) {
		System.out.println("AFTER_UNMARSHAL fired");
	}

}

		JAXBContext jaxbContext = JAXBContext.newInstance(Sample.class);
		Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
		// 设置监听器
		unmarshaller.setListener(new UnmarshallListener());
		StringReader r = new StringReader(xml);
		Sample sample = unmarshaller.unmarshal(new StreamSource(r), Sample.class).getValue();
		System.out.println(sample.toString());


(5) 注解

设置内部标签顺序(默认是按标签字母排序)
@XmlType(propOrder={"id", "name", "flag"})
@XmlRootElement
@XmlType(propOrder={"id", "name", "flag"})
public class SampleOrder {
	private int id;
	private String name;
	private boolean flag;
}

引用
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sampleOrder>
    <id>501</id>
    <name>marshallerTagOrder</name>
    <flag>false</flag>
</sampleOrder>


把字段值作为属性(默认都是标签)
@XmlAttribute
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class SampleAttribute {
	@XmlAttribute
	private int id;
	private String name;
	private boolean flag;
}

引用
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sampleAttribute id="502">
    <name>SampleAttribute</name>
    <flag>false</flag>
</sampleAttribute>


不输出某个字段值
@XmlTransient
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class SampleTransient {
	private int id;
	private String name;
	@XmlTransient
	private boolean flag;
}

引用
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sampleTransient>
    <id>503</id>
    <name>SampleTransient</name>
</sampleTransient>


空值处理

@XmlElement(nillable = true)

		SampleEmpty s = new SampleEmpty();
		s.setId(504);
		s.setName("SampleEmpty");
		s.setFlag(false);
		s.setName1(null);
		s.setName2("");
		s.setName3(null);

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class SampleEmpty {
	private int id;
	private String name;
	private boolean flag;
	private String name1;
	private String name2;
	@XmlElement(nillable = true)
	private String name3;
}

引用
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sampleEmpty>
    <id>504</id>
    <name>SampleEmpty</name>
    <flag>false</flag>
    <name2></name2>
    <name3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
</sampleEmpty>


变更输出标签属性名称

@XmlRootElement(name="foobar-tag")
@XmlAttribute(name="foobar-id")
@XmlElement(name="foobar-value")


@XmlRootElement(name="foobar-tag")
@XmlAccessorType(XmlAccessType.FIELD)
public class SampleName {
	@XmlAttribute(name="foobar-id")
	private int id;
	@XmlElement(name="foobar-value")
	private String name;
	private boolean flag;
}

引用
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<foobar-tag foobar-id="505">
    <foobar-value>SampleName</foobar-value>
    <flag>false</flag>
</foobar-tag>


Bean类型
@XmlRootElement
public class SampleBeanNested {
	private int id;
	private String name;
	private boolean flag;
	private People people;
}

public class People {
	private int id;
	private String name;
}

引用
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sampleBeanNested>
    <flag>false</flag>
    <id>506</id>
    <name>SampleBeanNested</name>
    <people>
        <id>100</id>
        <name>People</name>
    </people>
</sampleBeanNested>


List类型

【字符串】默认

@XmlRootElement
public class SampleStringList1 {
	private int id;
	private String name;
	private boolean flag;
	private List<String> emailAddresses;
}

引用
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sampleStringList1>
    <emailAddresses>a1.rensanning@gmail.com</emailAddresses>
    <emailAddresses>a2.rensanning@gmail.com</emailAddresses>
    <emailAddresses>a3.rensanning@gmail.com</emailAddresses>
    <flag>false</flag>
    <id>507</id>
    <name>SampleStringList1</name>
</sampleStringList1>


@XmlElementWrapper

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class SampleStringList2 {
	private int id;
	private String name;
	private boolean flag;
	@XmlElementWrapper(name="email-addresses")
	@XmlElement(name="email-address")
	private List<String> emailAddresses;
}

引用
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sampleStringList2>
    <id>508</id>
    <name>SampleStringList2</name>
    <flag>false</flag>
    <email-addresses>
        <email-address>b1.rensanning@gmail.com</email-address>
        <email-address>b2.rensanning@gmail.com</email-address>
        <email-address>b3.rensanning@gmail.com</email-address>
    </email-addresses>
</sampleStringList2>


@XmlList

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class SampleStringList3 {
	private int id;
	private String name;
	private boolean flag;
	@XmlList
	private List<String> emailAddresses;
}

引用
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sampleStringList3>
    <id>509</id>
    <name>SampleStringList3</name>
    <flag>false</flag>
    <emailAddresses>c1.rensanning@gmail.com c2.rensanning@gmail.com c3.rensanning@gmail.com</emailAddresses>
</sampleStringList3>


【Bean】默认

@XmlRootElement
public class SampleBeanList1 {
	private int id;
	private String name;
	private boolean flag;
	private List<People> peoples;
}

引用
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sampleBeanList1>
    <flag>false</flag>
    <id>510</id>
    <name>SampleBeanList1</name>
    <peoples>
        <id>100</id>
        <name>People1</name>
    </peoples>
    <peoples>
        <id>200</id>
        <name>People2</name>
    </peoples>
    <peoples>
        <id>300</id>
        <name>People3</name>
    </peoples>
</sampleBeanList1>


@XmlElementWrapper

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class SampleBeanList2 {
	private int id;
	private String name;
	private boolean flag;
	@XmlElementWrapper(name="list")
	@XmlElement(name="people")
	private List<People> peoples;
}

引用
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sampleBeanList2>
    <id>511</id>
    <name>SampleBeanList2</name>
    <flag>false</flag>
    <list>
        <people>
            <id>100</id>
            <name>People1</name>
        </people>
        <people>
            <id>200</id>
            <name>People2</name>
        </people>
        <people>
            <id>300</id>
            <name>People3</name>
        </people>
    </list>
</sampleBeanList2>


@XmlElements

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class SampleBeanList3 {
	private int id;
	private String name;
	private boolean flag;
	@XmlElementWrapper(name="list")
	@XmlElements( { 
    	@XmlElement(name = "man", type = Man.class),
		@XmlElement(name = "woman", type = Woman.class) 
	})
	private List<People> peoples;
}

引用
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sampleBeanList3>
    <id>512</id>
    <name>SampleBeanLis3</name>
    <flag>false</flag>
    <list>
        <man>
            <id>100</id>
            <name>Man</name>
            <attr1>Attr1</attr1>
        </man>
        <woman>
            <id>200</id>
            <name>Woman</name>
            <attr2>Attr2</attr2>
        </woman>
    </list>
</sampleBeanList3>


枚举类型

@XmlEnum、@XmlEnumValue

@XmlEnum(Integer.class)
@XmlType
public enum BloodType {
    @XmlEnumValue("1") A,
    @XmlEnumValue("2") B,
    @XmlEnumValue("3") O,
    @XmlEnumValue("4") AB
}

@XmlRootElement
public class SampleEnum {
	private int id;
	private String name;
	private boolean flag;
	private BloodType bloodType;
}

引用
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sampleEnum>
    <bloodType>2</bloodType>
    <flag>false</flag>
    <id>513</id>
    <name>SampleEnum</name>
</sampleEnum>


(6) 适配器

@XmlJavaTypeAdapter

public class DateAdapter extends XmlAdapter<String, Date> {

	private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss");

	public Date unmarshal(String v) throws Exception {
		return dateFormat.parse(v);
	}

	public String marshal(Date v) throws Exception {
		return dateFormat.format(v);
	}

}

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class SampleDateAdapter {
	private int id;
	private String name;
	private boolean flag;
	@XmlJavaTypeAdapter(DateAdapter.class)
	private Date date;
}

引用
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<sampleDateAdapter>
    <id>601</id>
    <name>SampleDateAdapter</name>
    <flag>false</flag>
    <date>2017/06/09 09:52:26</date>
</sampleDateAdapter>


(7) Schema检验

SampleSchema.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
     
    <xs:element name="SampleSchema">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="name" type="xs:string"  />
                <xs:element name="flag" type="xs:boolean" />
            </xs:sequence>
            <xs:attribute name="id" type="xs:int" />
        </xs:complexType>
    </xs:element> 
    
</xs:schema>


marshaller.setSchema();

@XmlRootElement(name="SampleSchema")
@XmlAccessorType(XmlAccessType.FIELD)
public class SampleSchema {
	@XmlAttribute
	private int id;
	private String name;
	private boolean flag;
}

		SampleSchema s = new SampleSchema();
		s.setId(701);
		s.setName("SampleSchema");
		s.setFlag(false);
		
		ClassLoader classLoader = new Main().getClass().getClassLoader();
        File schemaFile = new File(classLoader.getResource("SampleSchema.xsd").getFile());
		
		SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
		Schema schema = sf.newSchema(schemaFile);

		JAXBContext jaxbContext = JAXBContext.newInstance(SampleSchema.class);
		Marshaller marshaller = jaxbContext.createMarshaller();
		marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
		// 设置Schema
		marshaller.setSchema(schema);
		marshaller.marshal(s, System.out);

引用
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<SampleSchema id="701">
    <name>SampleSchema</name>
    <flag>false</flag>
</SampleSchema>


validator.validate(source);

		SampleSchema s = new SampleSchema();
		s.setId(702);
		s.setName("marshallerValidator");
		s.setFlag(false);

		ClassLoader classLoader = new Main().getClass().getClassLoader();
        File schemaFile = new File(classLoader.getResource("SampleSchema.xsd").getFile());
        
		SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
		Schema schema = sf.newSchema(schemaFile);

		JAXBContext jaxbContext = JAXBContext.newInstance(SampleSchema.class);
		
		// 校验XML
		JAXBSource source = new JAXBSource(jaxbContext, s);
        Validator validator = schema.newValidator();
        validator.setErrorHandler(new CustomValidationErrorHandler());
        validator.validate(source);

		Marshaller marshaller = jaxbContext.createMarshaller();
		marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
		marshaller.marshal(s, System.out);


(8) 捕获事件

marshaller.setEventHandler();

public class CustomEventHandler implements ValidationEventHandler {

	@Override
	public boolean handleEvent(ValidationEvent event) {
		System.out.println(event.getSeverity());

		if (event.getSeverity() == ValidationEvent.ERROR
				|| event.getSeverity() == ValidationEvent.FATAL_ERROR) {
			ValidationEventLocator locator = event.getLocator();
			System.out.println("Line:Col[" + locator.getLineNumber() + ":" + locator.getColumnNumber() + "]");
			throw new RuntimeException(event.getMessage(), event.getLinkedException());
		}
		return true;
	}

}

		JAXBContext jaxbContext = JAXBContext.newInstance(Sample.class);
		Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
		// 设置事件处理器
		unmarshaller.setEventHandler(new CustomEventHandler());
		StringReader r = new StringReader(xml);
		Sample sample = unmarshaller.unmarshal(new StreamSource(r), Sample.class).getValue();
		System.out.println(sample.toString());
分享到:
评论

相关推荐

    jaxb入门学习

    ### Jaxb入门学习知识点详解 #### 一、JAXB概念与作用 JAXB (Java Architecture for XML Binding) 是一种将XML数据与Java对象互相转换的技术。它通过编组(marshalling)和解组(unmarshalling)的过程,使得开发者...

    用JAXB生成一个XML

    然后,使用JAXB的`Marshaller`类将`CatalogType`实例序列化为XML文档并输出到控制台。 #### 六、总结 本文详细介绍了如何使用JAXB从XML模式生成Java类,并最终构建出符合该模式的XML文档。通过这种方式,可以有效...

    JAXB资料集.rar

    "JAXB的使用入门"这部分资料可能是引导读者逐步了解如何开始使用JAXB。通常,使用JAXB的步骤包括以下几个关键点: 1. **创建Java模型类**:根据XML Schema定义,创建相应的Java类,使用JAXB注解来标记字段与XML元素...

    Castor入门实例,含完整jar包

    这个入门实例包含了完整的Castor jar包,使初学者能够快速上手并理解其工作原理。 Castor的核心功能在于它允许开发者通过定义XML绑定文件,将XML结构映射到Java类。这使得XML处理变得简单,因为不再需要手动解析XML...

    XFire 实现web service入门实例源码

    本教程将深入探讨如何使用XFire来实现一个基础的Web服务入门实例,并通过源码分析来增进理解。 首先,我们要明白Web服务的基本概念。Web服务基于开放标准,如SOAP(Simple Object Access Protocol)、WSDL(Web ...

    WebService&SOA&ESB入门介绍(手把手xfire开发WS入门实例)

    XFire 1.x提供了一个简单易用的API,支持SOAP、WSDL和JAXB。Axis 1.x/2.x也是流行的Web Service开发工具,它包含了更多的功能和对不同版本SOAP的支持。 【AJAX】 AJAX(Asynchronous JavaScript and XML)是Web开发...

    cxf一个入门实例,用最新的cxf2.2.4开发的

    这个入门实例是基于CXF 2.2.4版本,一个较旧但仍然具有教育意义的版本,可以帮助初学者理解如何使用CXF来创建Web服务。 在CXF 2.2.4中,主要关注的特性包括: 1. **JAX-WS支持**:CXF支持Java API for XML Web ...

    jaxbRss:RSS对象的JAXB绑定

    4. **示例代码**:展示不同场景下的应用实例,如解析RSS feed,创建新的RSS条目,或者将RSS数据转换为其他格式。 5. **用户指南**:解释如何适应和扩展项目,以满足特定需求。 6. **故障排除**:常见问题解答和错误...

    Spring oxm入门实例

    Spring OXM入门实例 春季OXM(Object-XML Mapping)是 Spring 框架中的一个重要组件,负责在 Java 对象和 XML 文档之间进行转换。OXM 的主要目的是将 Java 对象转换为 XML 文档,以便于数据交换和存储。 Spring OXM...

    Java网络编程从入门到精通

    本教程“Java网络编程从入门到精通”将详细讲解这些概念,并通过实例代码说明如何在实践中应用。通过学习,你将掌握创建网络服务、处理网络请求、优化网络性能等技能,为你的Java开发生涯打下坚实的基础。无论是新手...

    axis2 webservice入门手册(JS,Java,PHP调用实例源码)www.sietoo.com出品

    ### Axis2 WebService 入门手册知识点详解 #### 一、Axis2简介 **1. AXIOM (AXIs Object Model)** - **定义**:AXIOM 是 Axis2 中用于处理 XML 的核心模型。它不同于传统的 DOM 和 SAX 解析方式,提供了更高效、...

    cxf入门例子

    8. **扩展与插件**:CXF有许多可扩展的特性,比如支持MTOM(Message Transmission Optimization Mechanism)和SwA(SwA: SOAP with Attachments),以及各种数据绑定机制如JAXB和XMLBeans。 9. **调试与日志**:CXF...

    cxf入门文档

    6. **数据绑定**:JAXB(Java Architecture for XML Binding)的使用,将XML与Java对象映射,简化数据交换。 7. **客户端调用**:如何创建CXF客户端,调用远程Web服务。 8. **安全与认证**:理解WS-Security,学习...

    xml从基础到精通实例教程

    - Java:DOM、SAX、StAX等API处理XML,JAXB用于对象与XML之间的映射。 - .NET:System.Xml命名空间提供各种XML处理工具,如XmlDocument和XmlNodeReader。 - PHP:DOM、SimpleXML、XMLReader等扩展处理XML,DOM更强大...

    xfire+spring+webservice入门例子

    【xfire+Spring+WebService 入门实例详解】 在IT行业中,Web服务是一个重要的通信方式,它允许不同系统间的应用程序进行数据交换。本入门实例将深入探讨如何使用XFire框架与Spring集成来构建和消费Web服务。XFire是...

    Web Service修炼之一XFire入门3

    1. **Spring框架**:XFire可以很好地与Spring框架集成,通过Spring的依赖注入管理服务实例,提供更好的可维护性和灵活性。 2. **JMS支持**:XFire还支持通过JMS(Java Message Service)进行异步通信,提高系统的可...

    Web Service 之 XFire入门

    在“Web Service 之 XFire入门”这篇文章中,博主可能还会分享如何通过实例代码来演示这些步骤,例如创建一个名为“helloworld”的简单服务,这个服务可能只有一个返回“Hello, World!”的函数。这样的例子有助于...

    Jbuilder开发WebService入门

    **Jbuilder开发WebService入门** Java Web Service(简称WebService)是一种基于标准协议的,可以在不同操作系统、不同编程语言之间进行通信的网络服务。Jbuilder作为早期的Java集成开发环境,提供了强大的支持来...

    CXF入门.rar

    通常,CXF允许你通过JAXB或XMLBeans等工具将Java对象自动绑定到XML消息,但在处理复杂数据结构时,需要正确地配置数据绑定和序列化设置。 【CXF - 江南白衣博物馆 - SpringSide Wiki】可能是一个关于CXF在...

Global site tag (gtag.js) - Google Analytics