`
pangwu86
  • 浏览: 118307 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

XBlink1.0.0版用户手册

 
阅读更多

公告:

由于googlecode的wiki编辑较为复杂,官网文档还需重新整理格式,这里先写一个1.0.0版的使用手册,让大家先了解下具体的使用。

 

用户API

 

对于XBlink作为普通用户只需要了解5或8个类即可,其他的都是内部使用,无需关心细节。


必备的:

两个核心类 XBlink.java XBConfig.java 

三个注解类 XBlinkAlias.java XBlinkAsAttribute.java XBlinkOmitField.java


XBlink


提供了序列化反序列方法,还有几个特殊的设置项。

 

PS:以下提及的输出指File,Ouputstream,Writer,输入指字符串,File,Inputstream,Readrer。


toXml


一种是只需一个参数(序列化的对象),结果返回对应XML字符串。

一种是需要两个参数(序列化的对象,输出),不再返回字符串,直接将结果写到输出。


fromXml


一种是只需一个参数(输入),根据输入将其提供的XML进行反序列化,返回对象。

一种是需要三个参数(输入,参考类,参考对象),其中参考类与参考对象可以二选其一,就是最终你想返回的类型是什么,你就放进去就好了,就会以此为依据,进行反序列操作,返回对象。

 

 

toAny 与 fromAny


这是一个扩展点,这个等后续版本实现了再详细讲解。


如果你特别想知道它能做什么


那么这样说吧,下面两个方法是等效的。

 

 

 

// 生成XML文件
XBlink.toXml(obj);

// 生成XML文件
XBlink.toAny(obj, "xml");
 

 


setGlobalXBConfig 与 setTransientXBConfig


设置运行时配置项,关于XBConfig请看后面介绍。

setGlobalXBConfig 是设置全局的,一直适用。

setTransientXBConfig 是设置一次性的,用过即抛,跟套套一个性质。

 

registerClassToBeUsed 与 registerClassesToBeUsed


注册你将要进行序列化的类。

如果不注册,XBlink是可以正常工作的,不过生成的XML会带有一部分类信息,看上去会不那么自然(怎么个不自然法你试一试便知),所以想要XML更合适阅读,推荐在一个static块中一次性注册所有可能要用的类。

例如:

 

static {
	// 注册一次即可
	Class<?>[] clzs = { Person.class, PhoneNumber.class };
	XBlink.registerClassesToBeUsed(clzs);
}
 

 

registerConverter


注册一个自定义转换器。

这个请参考后面的扩展点来理解。

 

XBConfig


提供了几个选项,可以设定你输出的文档一些特性。


newXBConfig

 

生成一个新的配置项。


setUseRelativePath


设置是否使用相对路径。默认是相对路径。


如果你序列化的类中存在循环引用的情况,那在写引用对象时,会输出类似下面这样一个节点。

 

<objA ref="../../objB/objA" />
 

 

 

ref记录的其相对当前节点的位置。


当然如果你设置使用绝对路径,那就会输入下面这样。

 

<objA ref="/objF/objE/objG/objB/objA" />
 

 

反正是可以帮你正确的找到引用对象的。


具体的实现思路可以看ME之前写的一篇文章

 

setIgnoreTransient


是否忽略设置为transient类型的字段。

默认忽略,因为JAVA的序列化就是这样认为的。

忽略的意思就是序列化时无视它,不多解释了。


setIgnoreNull


是否忽略值为null的字段。

默认忽略,也就是不输出了,因为反序列化时它也是null,也不用设置回去。

当然如果你非要在XML显示的告诉你这里有个字段并且它是空,那你就设置一下好了。


setCompact


设置生成的文本是否采用紧凑模式。

默认为否,会采用更适合人类阅读的格式,也就是有换行,制表符等标识的文档。

如果设为true,那你就会得到仅一行的文档,当然它的体积会小很多,也会让反序列化更快,所以更适合用在生产环境中。

 

——————————————————

后续版本中这个类会提供更多的可配置项


XBlinkAlias


可以在类上或字段上设置别名。


至于效果直接看下面,事实胜于雄辩。


定义一个Child类:

 

@XBlinkAlias("孩子")
public class Child {

        @XBlinkAlias("姓名")
        private String name;

        @XBlinkAlias("年龄")
        private int age;

	// 省略构造器与其他方法
}
 

 

测试用例:

 

Child child = new Child();
child.setName("Tom");
child.setAge(10);

System.out.println(XBlink.toXml(child));
 

 

 

结果:

 

<?xml version="1.0" encoding="UTF-8"?>
<孩子>
    <姓名>Tom</姓名>
    <年龄>10</年龄>
</孩子>
 

 

效果很好,有木有!

 

 

XBlinkOmitField

 

忽略的字段,在字段上设置后,序列化时就会无视他,不解释了。

 

XBlinkAsAttribute


这个应该是XML格式特有的,在一个节点上加上属性。


就用上面那个Child做例子。


修改下:

 

@XBlinkAlias("孩子")
public class Child {

        @XBlinkAlias("姓名")
	@XBlinkAsAttribute
        private String name;

        @XBlinkAlias("年龄")
        private int age;

	// 省略构造器与其他方法
}

 

 

还是那个测试用例,直接看结果:

 

<?xml version="1.0" encoding="UTF-8"?>
<孩子 姓名="Tom">
    <年龄>10</年龄>
</孩子>

 

 

 

—————————邪恶的分割线———————

 

好了,只要你看懂了以上的内容,了解了这5个类的用法,XBlink在使用上90%的问题你已经可以自己解决了,是不是很简单很好用呢。

 

下面将讲解剩下的10%,也就是可以自由扩展的地方。

 

—————————邪恶的分割线———————

 

扩展API

 

一个注解类:XBlinkConverter.java

两个转换器基类:Converter.java SingleValueTypeConverter.java

 

 

XBlinkConverter


在字段上设置后,该字段在序列化时将采用你提供的转换器进行工作。


举个例子哈。


一个日期类型,默认的转换器,生成的格式是 yyyy-MM-dd HH:mm:ss


你希望能使用另一种格式,比如仅需要日期且年在最后位置,格式是MM-dd-yyyy

 

那么你就需要实现一个类,并且继承SingleValueTypeConverter

 

例如下面这样:

 

/**
 * 测试用的一个java.util.Date类型转换器。
 * 
 * @author 胖五(pangwu86@gmail.com)
 */
public class MyDateConverter extends SingleValueTypeConverter {

	private static final String DATA_FORMAT_TEMPLATE = "MM-dd-yyyy";

	public Class<?>[] getTypes() {
		return new Class<?>[] { Date.class };
	}

	public boolean canConvert(Class<?> type) {
		return Date.class == type;
	}

	public String obj2Text(Object obj) throws Exception {
		SimpleDateFormat dateFormat = new SimpleDateFormat(DATA_FORMAT_TEMPLATE);
		return dateFormat.format(obj);
	}

	public Object text2Obj(String text) throws Exception {
		SimpleDateFormat dateFormat = new SimpleDateFormat(DATA_FORMAT_TEMPLATE);
		return dateFormat.parseObject(text);
	}

}

 

这样要说下前面的XBlink.registerConverter这个方法。


如果你仅仅想在某个日期上使用自定义格式,那么就加注解就可以了


如果你觉得默认的你想替换掉,所有有日期的地方都用你的格式,那就调用XBlink.registerConverter,它会根据替换掉默认实现。

 

也就是一个控制局部,一个控制全局,任君调遣。

 

 

Converter 与 SingleValueTypeConverter

 

这两个类,你简单看下估计就会明白了。

 

/**
 * 转换器,提供对象<--->文字相互转换的功能。
 * 
 * 注意,这里只负责两者的转换,不要加入输出等操作。
 * 
 * @author 胖五(pangwu86@gmail.com)
 */
public interface Converter {

	/**
	 * 返回当前转换器可以处理的对象类型。
	 * 
	 * @return class对象
	 */
	public Class<?>[] getTypes();

	/**
	 * 是否可以转换这种类型。
	 * 
	 * @param type
	 *            类类型
	 * @return 能否成功
	 */
	public boolean canConvert(Class<?> type);

	/**
	 * 是单值类型吗?
	 * 
	 * @return 判断结果
	 */
	public boolean isSingleValueType();

	/**
	 * 将对象转换为文字格式的值。
	 * 
	 * @param obj
	 *            转换对象
	 * @throws Exception
	 *             异常
	 * @return 字符结果
	 */
	public String obj2Text(Object obj) throws Exception;

	/**
	 * 将文字格式的值转换为对象。
	 * 
	 * @param text
	 *            文字格式的值
	 * @throws Exception
	 *             异常
	 * @return 对象
	 */
	public Object text2Obj(String text) throws Exception;

}

 

 

/**
 * 单值类型的共同基类。
 * 
 * @author 胖五(pangwu86@gmail.com)
 */
public abstract class SingleValueTypeConverter implements Converter {

	public boolean isSingleValueType() {
		return true;
	}

}

 

再结合下上面的MyDateConverter,相信你一定明白它的作用。


其实本来还提供一个FormValueTypeConverter类,也就是说可以在里面设置格式的转换器,这个更强大,但也更加难以控制,所以当前版本并没有开放出来,等后续版本有了好的解决方案再开放给大家。

 

 

—————————邪恶的分割线———————

以上就是ME现在能想到的基本用法了,基本上遵循着简单易用的原则来搞的,希望大家喜欢。


还有就是什么对象可以进行序列化,由于时间紧,默认提供的转换器还不够全面,所以有些类型会报错,不过如果你看懂了上面的自定义转换器这节,相信你可以自己解决的。


目前的版本,可以序列化java中常见的类(String,Date等),八种基本类型,枚举类型,一维数组(多维暂时不支持),List,Set,Map。


也就是说对简单的应用应该是富富有余了。


其他的想起什么再补充吧,欢迎提问或提出意见。

12
1
分享到:
评论
15 楼 han0917 2013-12-09  
问下把对象某属性A设置为"",即空串,序列化后xml显示为<A />,有没有办法显示成<A></A>
14 楼 Matol 2013-05-04  

初试了一下,还是很惊喜的。毕竟,程序员最清楚自己需要怎样的工具。
同时呢,还发现了一些问题,希望,能够更加完善,也是必备的功能。

1,文件的连缀用法。比如我用该工具将一个objec生成到了xml文件,然后,又用该工具获取到这个xml文件,再次写入另一个object对象。P:需求是写入的新object,追加在已有的xml文件相应节点之下,而不是重新替换了xml文件。

2,xml文件内容中的object对象,toList<Object> 到java

3,

13 楼 riosk 2012-05-30  
楼主:请问是否可以生成如下形式XML.

<?xml version="1.0" encoding="UTF-8"?> 
<姓名>Tom</姓名>
<年龄>10</年龄>

没有了<孩子>
12 楼 riosk 2012-05-30  
// 生成XML文件  
XBlink.toXml(obj);
11 楼 shenldz 2011-11-29  
楼主,您好。

在使用过程中发现一个问题,重现步骤如下:

1、建立两个javabean类:AXmlBean,BXmlBean, 使它们有相同的别名。

2、使用XBlink.registerClassesToBeUsed(Class<?>[])注册这两个类,注册的时候按顺序AXmlBean, BXmlBean。

3、使用XBlink.fromXml(CharSequence),根据String得到AXmlBean。此时会抛NullPointException。

Debug了一下,发现原因是由于AXmlBean,BXmlBean两者的别名相同,所以在注册时,放置类对象的map,由于key值是别名的String,所以对注册多个相同别名的javabean而言,最后注册的在该map中会覆盖掉前面的那些。我现在换成使用方法XBlink.fromXml(CharSequence, Class<?>, Object<?>),每次转换的时候指定被转换的类型。

请问下楼主,这个是我使用不当造成的问题,还是一个需要改进的bug。。。>_<,望答复。

另外,非常感谢楼主编写这个框架,并公开源码。。。祝新年快乐(虽然有点早。。。)
10 楼 鱼乐 2011-10-25  
请问如何闭合标签,
默认生成如下:
<?xml version="1.0" encoding="UTF-8"?>
<孩子 姓名="Tom">
</孩子>

是否可以设置生成如下形式:
<?xml version="1.0" encoding="UTF-8"?>
<孩子 姓名="Tom"/>
9 楼 sourceforge 2011-10-19  
JSON的版本有吗?
8 楼 yin_bp 2011-10-13  
引用
多维暂时不支持


楼主做好还是要支持多维数组啊,毕竟多维数组也是比较常用的数据类型哦,像bboss,xstream都是支持任意类型多维数组序列化/反序列化操作的。另外bboss还能够很好地支持File(二进制文件,文本文件)的序列化\反序列化操作,不知道xblink支持不。
7 楼 pangwu86 2011-10-10  
giianhui 写道
麻烦补充一下下载地址。

请看这里http://www.iteye.com/news/22986
最下面有下载地址
6 楼 giianhui 2011-10-10  
麻烦补充一下下载地址。
5 楼 pangwu86 2011-10-10  
yanweimin7 写道
能否添加将对象序列化为objectoutputstream导出的那样的格式的实现

objectoutputstream的话,定义的对象类必须实现java.io.Serializable接口
也就是JAVA序列化的工作了,跟XBlink的目标不一致。

XBlink序列化的结果,是可以让人阅读的文本格式,非二进制文本。
4 楼 yanweimin7 2011-10-10  
能否添加将对象序列化为objectoutputstream导出的那样的格式的实现
3 楼 pangwu86 2011-10-10  
pangwu86 写道
zozoh 写道
找时间,考虑写个 XIocLoader ......  

啥东东

哦,看明白了,呵呵
2 楼 pangwu86 2011-10-10  
zozoh 写道
找时间,考虑写个 XIocLoader ......  

啥东东
1 楼 zozoh 2011-10-10  
找时间,考虑写个 XIocLoader ......  

相关推荐

    xblink 关键代码

    * XBlink最NB的反序列方法,生成你需要的对象。 * * * 根据docTypeName与其格式特点,进行反序列化生成对象。 * * @param cs * 字符信息 * @param docTypeName * 任意你想要的文档格式...

    智慧园区3D可视化解决方案PPT(24页).pptx

    在智慧园区建设的浪潮中,一个集高效、安全、便捷于一体的综合解决方案正逐步成为现代园区管理的标配。这一方案旨在解决传统园区面临的智能化水平低、信息孤岛、管理手段落后等痛点,通过信息化平台与智能硬件的深度融合,为园区带来前所未有的变革。 首先,智慧园区综合解决方案以提升园区整体智能化水平为核心,打破了信息孤岛现象。通过构建统一的智能运营中心(IOC),采用1+N模式,即一个智能运营中心集成多个应用系统,实现了园区内各系统的互联互通与数据共享。IOC运营中心如同园区的“智慧大脑”,利用大数据可视化技术,将园区安防、机电设备运行、车辆通行、人员流动、能源能耗等关键信息实时呈现在拼接巨屏上,管理者可直观掌握园区运行状态,实现科学决策。这种“万物互联”的能力不仅消除了系统间的壁垒,还大幅提升了管理效率,让园区管理更加精细化、智能化。 更令人兴奋的是,该方案融入了诸多前沿科技,让智慧园区充满了未来感。例如,利用AI视频分析技术,智慧园区实现了对人脸、车辆、行为的智能识别与追踪,不仅极大提升了安防水平,还能为园区提供精准的人流分析、车辆管理等增值服务。同时,无人机巡查、巡逻机器人等智能设备的加入,让园区安全无死角,管理更轻松。特别是巡逻机器人,不仅能进行360度地面全天候巡检,还能自主绕障、充电,甚至具备火灾预警、空气质量检测等环境感知能力,成为了园区管理的得力助手。此外,通过构建高精度数字孪生系统,将园区现实场景与数字世界完美融合,管理者可借助VR/AR技术进行远程巡检、设备维护等操作,仿佛置身于一个虚拟与现实交织的智慧世界。 最值得关注的是,智慧园区综合解决方案还带来了显著的经济与社会效益。通过优化园区管理流程,实现降本增效。例如,智能库存管理、及时响应采购需求等举措,大幅减少了库存积压与浪费;而设备自动化与远程监控则降低了维修与人力成本。同时,借助大数据分析技术,园区可精准把握产业趋势,优化招商策略,提高入驻企业满意度与营收水平。此外,智慧园区的低碳节能设计,通过能源分析与精细化管理,实现了能耗的显著降低,为园区可持续发展奠定了坚实基础。总之,这一综合解决方案不仅让园区管理变得更加智慧、高效,更为入驻企业与员工带来了更加舒适、便捷的工作与生活环境,是未来园区建设的必然趋势。

    labelme标注的json转mask掩码图,用于分割数据集 批量转化,生成cityscapes格式的数据集

    labelme标注的json转mask掩码图,用于分割数据集 批量转化,生成cityscapes格式的数据集

    (参考GUI)MATLAB GUI漂浮物垃圾分类检测.zip

    (参考GUI)MATLAB GUI漂浮物垃圾分类检测.zip

    人脸识别_OpenCV_活体检测_证件照拍照_Demo_1741778955.zip

    人脸识别项目源码实战

    人脸识别_科大讯飞_Face_签到系统_Swface_1741770704.zip

    人脸识别项目实战

    跟网型逆变器小干扰稳定性分析与控制策略优化simulink仿真模型和代码.zip

    本仿真模型基于MATLAB/Simulink(版本MATLAB 2016Rb)软件。建议采用matlab2016 Rb及以上版本打开。(若需要其他版本可联系代为转换) CSDN详情地址:https://blog.csdn.net/qq_50594161/article/details/146242453sharetype=blogdetail&sharerId=146242453&sharerefer=PC&sharesource=qq_50594161&spm=1011.2480.3001.8118

    16-1文本表示&词嵌入.ipynb

    实战练习分词、创建词表、文本处理

    45页-零碳智慧园区标准解决方案:模块化、可扩展且可复制的解决方案.pdf

    在智慧园区建设的浪潮中,一个集高效、安全、便捷于一体的综合解决方案正逐步成为现代园区管理的标配。这一方案旨在解决传统园区面临的智能化水平低、信息孤岛、管理手段落后等痛点,通过信息化平台与智能硬件的深度融合,为园区带来前所未有的变革。 首先,智慧园区综合解决方案以提升园区整体智能化水平为核心,打破了信息孤岛现象。通过构建统一的智能运营中心(IOC),采用1+N模式,即一个智能运营中心集成多个应用系统,实现了园区内各系统的互联互通与数据共享。IOC运营中心如同园区的“智慧大脑”,利用大数据可视化技术,将园区安防、机电设备运行、车辆通行、人员流动、能源能耗等关键信息实时呈现在拼接巨屏上,管理者可直观掌握园区运行状态,实现科学决策。这种“万物互联”的能力不仅消除了系统间的壁垒,还大幅提升了管理效率,让园区管理更加精细化、智能化。 更令人兴奋的是,该方案融入了诸多前沿科技,让智慧园区充满了未来感。例如,利用AI视频分析技术,智慧园区实现了对人脸、车辆、行为的智能识别与追踪,不仅极大提升了安防水平,还能为园区提供精准的人流分析、车辆管理等增值服务。同时,无人机巡查、巡逻机器人等智能设备的加入,让园区安全无死角,管理更轻松。特别是巡逻机器人,不仅能进行360度地面全天候巡检,还能自主绕障、充电,甚至具备火灾预警、空气质量检测等环境感知能力,成为了园区管理的得力助手。此外,通过构建高精度数字孪生系统,将园区现实场景与数字世界完美融合,管理者可借助VR/AR技术进行远程巡检、设备维护等操作,仿佛置身于一个虚拟与现实交织的智慧世界。 最值得关注的是,智慧园区综合解决方案还带来了显著的经济与社会效益。通过优化园区管理流程,实现降本增效。例如,智能库存管理、及时响应采购需求等举措,大幅减少了库存积压与浪费;而设备自动化与远程监控则降低了维修与人力成本。同时,借助大数据分析技术,园区可精准把握产业趋势,优化招商策略,提高入驻企业满意度与营收水平。此外,智慧园区的低碳节能设计,通过能源分析与精细化管理,实现了能耗的显著降低,为园区可持续发展奠定了坚实基础。总之,这一综合解决方案不仅让园区管理变得更加智慧、高效,更为入驻企业与员工带来了更加舒适、便捷的工作与生活环境,是未来园区建设的必然趋势。

    人脸识别_活体检测_数据录入_登录系统Face_Login_1741778308.zip

    人脸识别项目源码实战

    学生信息管理平台是一个基于Java Web技术的综合性管理平台

    学生信息管理系统是一个基于Java Web技术的综合性管理平台。通过此系统,可以实现对学生、教师、选课信息等的动态管理, 提升学校管理效率。系统采用分层架构设计,前端使用HTML、CSS,JavaScript和jQuery,后端基于Servlet,JSP和Spring框架,数据库采用MySQL。主要有四个大功能,学生管理( 增加学生信息、删除学生信息、修改学生信息、查询学生信息)、教师管理(增加教师信息、删除教师信息、修改教师信息、查询教师信息)、选课信息管理(添加选课、查询选课情况、删除选课记录)、系统管理( 登录与注册功能、 用户角色管理(老师,学生,管理员)、系统日志查看)。 技术架构 1.前端技术 HTML,CSS:静态页面布局与样式 JavaScript,jQuery:动态交互、DOM操作和AJAX请求 2.后端技术 Servlet:控制层,处理用户请求 JSP:页面动态生成 Spring:依赖注入,业务逻辑分离 3.数据库 MySQL:存储学生、教师,课程等数据 JDBC:数据库连接与操作

    PHP进阶系列之Swoole入门精讲(课程视频)

    本课程是 PHP 进阶系列之 Swoole 入门精讲,系统讲解 Swoole 在 PHP 高性能开发中的应用,涵盖 协程、异步编程、WebSocket、TCP/UDP 通信、任务投递、定时器等核心功能。通过理论解析和实战案例相结合,帮助开发者掌握 Swoole 的基本使用方法及其在高并发场景下的应用。 适用人群: 适合 有一定 PHP 基础的开发者、希望提升后端性能优化能力的工程师,以及 对高并发、异步编程感兴趣的学习者。 能学到什么: 掌握 Swoole 基础——理解 Swoole 的核心概念,如协程、异步编程、事件驱动等。 高并发处理——学习如何使用 Swoole 构建高并发的 Web 服务器、TCP/UDP 服务器。 实战项目经验——通过案例实践,掌握 Swoole 在 WebSocket、消息队列、微服务等场景的应用。 阅读建议: 建议先掌握 PHP 基础,了解 HTTP 服务器和并发处理相关概念。学习过程中,结合 官方文档和实际项目 进行实践,加深理解,逐步提升 Swoole 开发能力。

    人脸识别_表情分析_spider运行_数据采集用途_1741771318.zip

    人脸识别项目实战

    美颜_GPUimage_人脸识别_动态贴纸_Demo_1741771705.zip

    人脸识别项目实战

    人脸照片文件批量分辨率裁剪工具

    功能简介:本工具可实现批量对照片文件的人脸识别,并按指定分辨率进行转换保存。 可为人脸识别采集系统提供很好的辅助工具。 软件基本于OPENVC开发,识别精确,转换高效。 人脸识别工具 +人脸采集处理

    基于强化学习与肌肉长度反馈控制的高效无意识姿态稳定算法研究(可复现,有问题请联系博主)

    内容概要:本文探讨了利用肌长变化反馈控制(FCM-ML)和演员-评论家强化学习(ACRL-NGN)来有效实现人体上肢和下肢无意识姿态稳定的算法方法。通过构建一个包含949条肌肉和22个关节的全身计算模型,在不同初始姿势的情况下进行模拟试验,验证了这些方法的有效性和鲁棒性,结果显示FCM-ML方法比其他传统方法更适用于此类任务。研究指出人类及其他脊椎动物在无意识状态下,通过抗拮抗性的肌肉长度变化反馈机制来维持舒适状态下的自然身体姿势(NBP)。此外,研究还表明这种控制策略有助于机器人设计、运动员训练以及康复患者的治疗。 适用人群:生物力学、机器人学以及神经科学领域的研究人员、工程师,以及关注人体姿态控制及其应用的学者和技术人员。 使用场景及目标:①解释人和非人的脊椎动物如何在无意识情况下维持最佳姿势,特别是处于重力环境中的自然身体姿势(NBP)。②为机器人肌肉控制提供理论支持和发展方向,特别是在模拟多肌肉协调控制方面。③指导运动训练及病患恢复计划的设计与优化。 其他说明:研究发现ACRL-NGN结合FCM-ML不仅能够迅速有效地实现期望的姿态稳定性,而且不需要对肌肉分类,这使其在复

    反编译apk重要的工具之一

    反编译apk重要的工具之一

    `计算机视觉_Python_PyQt5_Opencv_综合图像处理与识别跟踪系统`.zip

    人脸识别项目实战

Global site tag (gtag.js) - Google Analytics