refactor: WordPress data import (#506)

* refactor: WordPress data import

* refactor: change wordpress convert class to interface and add wordpress convert null judgment

* fix: add some class doc

* fix: exclude commons io

* feat: add custom annotation: @PropertyMappingTo and reflection utils, It can be used to mapping the relationship between source model and target model

* feat: replace the original wordpressconvert implementation with a custom annotation

* fix: change wordpress convert method name

* fix: change wordpress convert method name

* fix: merger conflict
pull/523/head
guqing 2020-01-20 15:23:48 +08:00 committed by John Niang
parent 4be31d8923
commit 61f0748a6a
16 changed files with 746 additions and 141 deletions

View File

@ -60,6 +60,8 @@ ext {
flywayVersion = '6.1.0'
h2Version = '1.4.196'
levelDbVersion = '0.12'
jsonVersion = '20190722'
fastJsonVersion = '1.2.56'
}
dependencies {
@ -104,6 +106,9 @@ dependencies {
implementation "net.sf.image4j:image4j:$image4jVersion"
implementation "org.flywaydb:flyway-core:$flywayVersion"
implementation "org.json:json:$jsonVersion"
implementation "com.alibaba:fastjson:$fastJsonVersion"
implementation "org.iq80.leveldb:leveldb:$levelDbVersion"
runtimeOnly "com.h2database:h2:$h2Version"
runtimeOnly 'mysql:mysql-connector-java'

View File

@ -1,29 +1,23 @@
package run.halo.app.handler.migrate;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Element;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;
import run.halo.app.exception.ServiceException;
import run.halo.app.model.entity.BasePost;
import run.halo.app.model.entity.Category;
import run.halo.app.model.entity.Post;
import run.halo.app.model.entity.Tag;
import run.halo.app.handler.migrate.converter.Converter;
import run.halo.app.handler.migrate.converter.WordPressConverter;
import run.halo.app.handler.migrate.support.vo.PostVO;
import run.halo.app.handler.migrate.support.wordpress.Rss;
import run.halo.app.model.enums.MigrateType;
import run.halo.app.service.*;
import run.halo.app.utils.MarkdownUtils;
import run.halo.app.utils.WordPressMigrateUtils;
import run.halo.app.utils.XmlMigrateUtils;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* WordPress migrate handler
@ -90,138 +84,21 @@ public class WordPressMigrateHandler implements MigrateHandler {
public void migrate(MultipartFile file) {
try {
String migrationContent = FileCopyUtils.copyToString(new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8));
Element rootElement = WordPressMigrateUtils.getRootElement(new FileInputStream(migrationContent));
Map<String, Object> resultSetMapping = WordPressMigrateUtils.getResultSetMapping(rootElement);
String jsonString = XmlMigrateUtils.xml2jsonString(new FileInputStream(migrationContent));
JSONObject json = JSONObject.parseObject(jsonString);
Rss rss = json.getObject("rss", Rss.class);
// Handle categories
List<Category> categories = handleCategories(resultSetMapping.get("wp:category"));
// 转换
Converter<Rss, List<PostVO>> converter = new WordPressConverter();
// Handle tags
List<Tag> tags = handleTags(resultSetMapping.get("wp:tag"));
List<PostVO> postVoList = converter.convertFrom(rss);
// Handle posts
List<BasePost> posts = handlePosts(resultSetMapping.get("item"));
log.debug("Migrated posts: [{}]", posts);
log.debug("Migrated posts: [{}]", postVoList);
} catch (Exception e) {
throw new ServiceException("WordPress 出文件 " + file.getOriginalFilename() + " 读取失败", e);
throw new ServiceException("WordPress 入出文件 " + file.getOriginalFilename() + " 读取失败", e);
}
}
private List<Category> handleCategories(@Nullable Object categoriesObject) {
if (!(categoriesObject instanceof List)) {
return Collections.emptyList();
}
List<Object> categoryObjectList = (List<Object>) categoriesObject;
List<Category> result = new LinkedList<>();
categoryObjectList.forEach(categoryObject -> {
if (!(categoryObject instanceof Map)) {
return;
}
Map<String, Object> categoryMap = (Map<String, Object>) categoryObject;
String slugName = categoryMap.getOrDefault("wp:category_nicename", "").toString();
Category category = categoryService.getBySlugName(slugName);
if (null == category) {
category = new Category();
category.setName(categoryMap.getOrDefault("wp:cat_name", "").toString());
category.setSlugName(slugName);
category = categoryService.create(category);
}
try {
result.add(category);
} catch (Exception e) {
log.warn("Failed to migrate a category", e);
}
});
return result;
}
private List<Tag> handleTags(@Nullable Object tagsObject) {
if (!(tagsObject instanceof List)) {
return Collections.emptyList();
}
List<Object> tagObjectList = (List<Object>) tagsObject;
List<Tag> result = new LinkedList<>();
tagObjectList.forEach(tagObject -> {
if (!(tagObject instanceof Map)) {
return;
}
Map<String, Object> tagMap = (Map<String, Object>) tagObject;
String slugName = tagMap.getOrDefault("wp:tag_slug", "").toString();
Tag tag = tagService.getBySlugName(slugName);
if (null == tag) {
tag = new Tag();
tag.setName(tagMap.getOrDefault("wp:tag_name", "").toString());
tag.setSlugName(slugName);
tag = tagService.create(tag);
}
try {
result.add(tag);
} catch (Exception e) {
log.warn("Failed to migrate a tag", e);
}
});
return result;
}
@NonNull
private List<BasePost> handlePosts(@Nullable Object postsObject) {
if (!(postsObject instanceof List)) {
return Collections.emptyList();
}
List<Object> postObjectList = (List<Object>) postsObject;
List<BasePost> result = new LinkedList<>();
postObjectList.forEach(postObject -> {
if (!(postObject instanceof Map)) {
return;
}
Map<String, Object> postMap = (Map<String, Object>) postObject;
BasePost post = new BasePost();
post.setTitle(postMap.getOrDefault("title", "").toString());
post.setUrl(postMap.getOrDefault("wp:post_name", "").toString());
post.setOriginalContent(MarkdownUtils.renderMarkdown(postMap.getOrDefault("content:encoded", "").toString()));
post.setFormatContent(postMap.getOrDefault("content:encoded", "").toString());
post.setSummary(postMap.getOrDefault("excerpt:encoded", "").toString());
String url = postMap.getOrDefault("wp:post_name", "").toString();
Post temp = postService.getByUrl(url);
if (temp != null) {
post.setUrl(post.getUrl() + "_1");
}
});
return null;
}
@Override
public boolean supportType(MigrateType type) {
return MigrateType.WORDPRESS.equals(type);

View File

@ -0,0 +1,45 @@
package run.halo.app.handler.migrate.converter;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* ,SOURCETARGET
*
* @author guqing
* @date 2020-01-18 16:45
*/
public interface Converter<SOURCE, TARGET> {
/**
* sourcetarget
*
* @param s
* @return
*/
TARGET convertFrom(SOURCE s);
/**
* SOURCETARGET
*
* @param s source,SOURCE
* @param function
* @return TARGET
*/
default TARGET convertFrom(SOURCE s, Function<SOURCE, TARGET> function) {
return function.apply(s);
}
/**
* SOURCETARGET
*
* @param list SOURCE
* @param function
* @return TARGET
*/
default List<TARGET> batchConverterFromDto(List<SOURCE> list, Function<SOURCE, TARGET> function) {
return list.stream().map(s -> convertFrom(s, function)).collect(Collectors.toList());
}
}

View File

@ -0,0 +1,121 @@
package run.halo.app.handler.migrate.converter;
import run.halo.app.handler.migrate.support.vo.PostVO;
import run.halo.app.handler.migrate.support.wordpress.*;
import run.halo.app.handler.migrate.utils.RelationMapperUtils;
import run.halo.app.model.entity.BaseComment;
import run.halo.app.model.entity.BasePost;
import run.halo.app.model.entity.Category;
import run.halo.app.model.entity.Tag;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
/**
* WordPress
*
* @author guqing
* @date 2020-01-18 16:50
*/
public class WordPressConverter implements Converter<Rss, List<PostVO>> {
@Override
public List<PostVO> convertFrom(Rss rss) {
return convertFrom(rss, this::apply);
}
/**
*
*/
public List<PostVO> apply(Rss rss) {
if (Objects.isNull(rss) || Objects.isNull(rss.getChannel())) {
return new ArrayList<>();
}
Channel channel = rss.getChannel();
List<Item> items = channel.getItems();
if (items == null) {
return new ArrayList<>();
}
return getBasePost(items);
}
private List<PostVO> getBasePost(List<Item> items) {
List<PostVO> posts = new ArrayList<>();
if (items == null) {
return posts;
}
for (Item item : items) {
PostVO postVo = new PostVO();
// 设置文章
BasePost post = getBasePostFromItem(item);
postVo.setBasePost(post);
// 获取标签和分类
List<WpCategory> categories = item.getCategories();
List<Category> categoryModelList = new ArrayList<>();
List<Tag> tags = new ArrayList<>();
if (categories != null) {
categories.forEach(category -> {
String domain = category.getDomain();
if ("post_tag".equals(domain)) {
Tag tag = RelationMapperUtils.convertFrom(category, Tag.class);
tags.add(tag);
} else if ("category".equals(domain)) {
Category categoryModel = RelationMapperUtils.convertFrom(category, Category.class);
categoryModelList.add(categoryModel);
}
});
}
// 设置标签和分类
postVo.setCategories(categoryModelList);
postVo.setTags(tags);
// 设置评论
List<BaseComment> comments = getCommentsFromItem(item);
postVo.setComments(comments);
posts.add(postVo);
}
return posts;
}
private List<BaseComment> getCommentsFromItem(Item item) {
List<BaseComment> baseComments = new ArrayList<>();
if (Objects.isNull(item) || Objects.isNull(item.getComments())) {
return baseComments;
}
List<Comment> comments = item.getComments();
for (Comment comment : comments) {
BaseComment baseComment = RelationMapperUtils.convertFrom(comment, BaseComment.class);
baseComments.add(baseComment);
}
return baseComments;
}
private BasePost getBasePostFromItem(Item item) {
BasePost post = RelationMapperUtils.convertFrom(item, BasePost.class);
String postDate = item.getPostDate();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
if (postDate != null) {
LocalDateTime dateTime = LocalDateTime.parse(postDate, formatter);
Date date = Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
post.setEditTime(date);
// 设置url为文章编辑时的时间毫秒数
post.setUrl(post.getEditTime() + "");
} else {
post.setUrl(System.currentTimeMillis() + "");
}
return post;
}
}

View File

@ -0,0 +1,21 @@
package run.halo.app.handler.migrate.support.vo;
import lombok.Data;
import run.halo.app.model.entity.BaseComment;
import run.halo.app.model.entity.BasePost;
import run.halo.app.model.entity.Category;
import run.halo.app.model.entity.Tag;
import java.util.List;
/**
* @author guqing
* @date 2020-01-18 16:52
*/
@Data
public class PostVO {
private BasePost basePost;
private List<Tag> tags;
private List<Category> categories;
private List<BaseComment> comments;
}

View File

@ -0,0 +1,28 @@
package run.halo.app.handler.migrate.support.wordpress;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import java.util.List;
/**
* <p>
* WordPressxmlchannel
* </p>
*
* @author guqing
* @date 2019-11-17 13:57
*/
@Data
public class Channel {
@JSONField(name = "title")
private String title;
private String link;
private String description;
private String pubDate;
private String language;
private String author;
@JSONField(name = "item")
private List<Item> items;
}

View File

@ -0,0 +1,53 @@
package run.halo.app.handler.migrate.support.wordpress;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import run.halo.app.handler.migrate.utils.PropertyMappingTo;
/**
* <p>
* WordPressxmlcomment,
* {@link run.halo.app.model.entity.PostComment}
* </p>
*
* @author guqing
* @date 2019-11-17 14:01
*/
@Data
public class Comment {
@JSONField(name = "wp:comment_id")
private String commentId;
@JSONField(name = "wp:comment_author")
@PropertyMappingTo("author")
private String commentAuthor;
@JSONField(name = "wp:comment_author_email")
@PropertyMappingTo("email")
private String commentAuthorEmail;
@JSONField(name = "wp:comment_author_url")
@PropertyMappingTo("authorUrl")
private String commentAuthorUrl;
@JSONField(name = "wp:comment_author_IP")
@PropertyMappingTo("ipAddress")
private String commentAuthorIp;
@JSONField(name = "wp:comment_date")
private String commentDate;
@JSONField(name = "wp:comment_content")
@PropertyMappingTo("content")
private String commentContent;
@JSONField(name = "wp:comment_approved")
private String commentApproved;
@JSONField(name = "wp:comment_parent")
@PropertyMappingTo("parentId")
private Long commentParent;
@JSONField(name = "wp:comment_user_id")
private String commentUserId;
}

View File

@ -0,0 +1,54 @@
package run.halo.app.handler.migrate.support.wordpress;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import run.halo.app.handler.migrate.utils.PropertyMappingTo;
import run.halo.app.model.entity.BasePost;
import java.util.List;
/**
* <p> WordPressxmlitem,{@link BasePost} </p>
*
* @author guqing
* @date 2019-11-17 13:59
*/
@Data
public class Item {
private String title;
private String link;
private String pubDate;
@JSONField(name = "post_date")
private String postDate;
@JSONField(name = "dc:creator")
private String creator;
private String description;
@JSONField(name = "content:encoded")
@PropertyMappingTo("formatContent")
private String content;
@JSONField(name = "wp:comment_status")
private String commentStatus;
@JSONField(name = "wp:status")
private String status;
@JSONField(name = "wp:post_password")
@PropertyMappingTo("password")
private String postPassword;
@JSONField(name = "wp:is_sticky")
private Integer isSticky;
@JSONField(name = "wp:comment")
private List<Comment> comments;
@JSONField(name = "category")
private List<WpCategory> categories;
}

View File

@ -0,0 +1,15 @@
package run.halo.app.handler.migrate.support.wordpress;
import lombok.Data;
/**
* WordPressxmlrsschannel
* RssChannel,
*
* @author guqing
* @date 2020-01-17 00:45
*/
@Data
public class Rss {
private Channel channel;
}

View File

@ -0,0 +1,23 @@
package run.halo.app.handler.migrate.support.wordpress;
import lombok.Data;
import run.halo.app.handler.migrate.utils.PropertyMappingTo;
import run.halo.app.model.entity.PostCategory;
/**
* WordPressxmlcategory,
* {@link PostCategory}
*
* @author guqing
* @date 2020-01-18 16:09
*/
@Data
public class WpCategory {
private String domain;
@PropertyMappingTo("slugName")
private String nicename;
@PropertyMappingTo("name")
private String content;
}

View File

@ -0,0 +1,19 @@
package run.halo.app.handler.migrate.utils;
import java.lang.annotation.*;
/**
*
* @author guqing
* @date 2020-1-19 13:51
*/
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PropertyMappingTo {
/**
* value
* @return
*/
String value() default "";
}

View File

@ -0,0 +1,128 @@
package run.halo.app.handler.migrate.utils;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
* ,使@PropertyMappingTo
*
* @author guqing
* @date 2020-01-19 01:47
*/
public class RelationMapperUtils {
private RelationMapperUtils() {
}
public static <SOURCE, TARGET> TARGET convertFrom(SOURCE source, Class<TARGET> targetClazz) {
Map<String, Class> methodNameAndTypeMap = new HashMap<>(16);
TARGET target;
try {
// 通过类的详情信息,创建目标对象 这一步等同于Hello target = new Hello()
target = targetClazz.getConstructor().newInstance();
Field[] targetFields = targetClazz.getDeclaredFields();
for (Field field : targetFields) {
methodNameAndTypeMap.put(field.getName(), field.getType());
}
} catch (Exception e) {
throw new RuntimeException("目标对象创建失败");
}
return propertyMapper(source, target, methodNameAndTypeMap);
}
private static <SOURCE, TARGET> TARGET propertyMapper(SOURCE source, TARGET target, Map<String, Class> methodNameAndTypeMap) {
// 判断传入源数据是否为空,如果空,则抛自定义异常
if (null == source) {
throw new IllegalArgumentException("数据源不能为空");
}
// 获取源对象的类的详情信息
Class<?> sourceClazz = source.getClass();
// 获取源对象的所有属性
Field[] sourceFields = sourceClazz.getDeclaredFields();
// 循环取到源对象的单个属性
for (Field sourceField : sourceFields) {
boolean sourceFieldHasAnnotation = sourceField.isAnnotationPresent(PropertyMappingTo.class);
String sourceFieldName = sourceField.getName();
// 如果源对象没有添加注解则默认使用源对象属性名去寻找对于的目标对象属性名
if (sourceFieldHasAnnotation) {
PropertyMappingTo propertyMapping = sourceField.getAnnotation(PropertyMappingTo.class);
String targetFieldName = propertyMapping.value();
copyProperty(sourceFieldName, targetFieldName, source, target, sourceField.getType(), methodNameAndTypeMap);
} else if (methodNameAndTypeMap.containsKey(sourceFieldName)) {
// 如果源对象和目标对象的属性名相同则直接设置
// 获取目标对象的属性名将属性名首字母大写拼接如setUsername、setId的字符串
// 判断源对象的属性名、属性类型是否和目标对象的属性名、属性类型一致
copyProperty(sourceFieldName, sourceFieldName, source, target, sourceField.getType(), methodNameAndTypeMap);
}
}
// 返回赋值得到的目标对象
return target;
}
private static <SOURCE, TARGET> void copyProperty(String sourceFieldName, String targetFieldName,
SOURCE source, TARGET target, Class<?> sourceType,
Map<String, Class> methodNameAndTypeMap) {
try {
Class<?> sourceClazz = source.getClass();
Class<?> targetClazz = target.getClass();
// 获取源对象的属性名将属性名首字母大写拼接如getUsername、getId的字符串
String sourceGetMethodName = getterName(sourceFieldName);
// 获得属性的get方法
Method sourceMethod = sourceClazz.getMethod(sourceGetMethodName);
// 调用get方法
Object sourceFieldValue = sourceMethod.invoke(source);
Class methodType = methodNameAndTypeMap.get(targetFieldName);
// 通过字段名称得到set方法名称
String targetSetMethodName = setterName(targetFieldName);
if (methodType == sourceType) {
// 调用方法并将源对象get方法返回值作为参数传入
Method targetSetMethod = targetClazz.getMethod(targetSetMethodName, sourceType);
targetSetMethod.invoke(target, sourceFieldValue);
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("转换失败,请检查属性类型是否匹配");
}
}
/**
* getset
*
* @param fieldName
* @param methodType :getset
* @return getset
*/
private static String getMethodNameFromField(String fieldName, String methodType) {
if (fieldName == null || fieldName.length() == 0) {
return null;
}
/*
* If the second char is upper, make 'get' + field name as getter name. For example, eBlog -> getBlog
*/
if (fieldName.length() > 2) {
String second = fieldName.substring(1, 2);
if (second.equals(second.toUpperCase())) {
return methodType + fieldName;
}
}
// Common situation
fieldName = methodType + fieldName.substring(0, 1).toUpperCase() +
fieldName.substring(1);
return fieldName;
}
private static String getterName(String fieldName) {
return getMethodNameFromField(fieldName, "get");
}
private static String setterName(String fieldName) {
return getMethodNameFromField(fieldName, "set");
}
}

View File

@ -9,13 +9,10 @@ import java.io.FileInputStream;
import java.util.*;
/**
* WordPress XML Map
*
* @author guqing
* @date 2019-10-29 14:49
* @date 2020-01-18 14:06
*/
public class WordPressMigrateUtils {
/**
* List
*/

View File

@ -0,0 +1,23 @@
package run.halo.app.utils;
import org.apache.commons.io.IOUtils;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.XML;
import java.io.FileInputStream;
import java.io.IOException;
/**
* <p> xmljson </p>
*
* @author guqing
* @date 2019-11-17 13:33
*/
public class XmlMigrateUtils {
public static String xml2jsonString(FileInputStream in) throws JSONException, IOException {
String xml = IOUtils.toString(in, "UTF-8");
JSONObject jsonObject = XML.toJSONObject(xml);
return jsonObject.toString();
}
}

View File

@ -0,0 +1,52 @@
package run.halo.app.utils;
import com.alibaba.fastjson.JSONObject;
import org.junit.jupiter.api.Test;
import org.springframework.util.ResourceUtils;
import run.halo.app.handler.migrate.converter.Converter;
import run.halo.app.handler.migrate.converter.WordPressConverter;
import run.halo.app.handler.migrate.support.vo.PostVO;
import run.halo.app.handler.migrate.support.wordpress.Rss;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;
/**
* @author guqing
* @date 2020-01-18 14:02
*/
public class XmlMigrateUtilsTest {
@Test
void testXmlMigrateUtils() throws IOException, URISyntaxException {
JSONObject json = readXml();
System.out.println("WordPress blog json data:" + json);
Rss rss = json.getObject("rss", Rss.class);
// System.out.println(rss);
}
@Test
void testWordPressConvert() throws IOException, URISyntaxException {
JSONObject json = readXml();
Rss rss = json.getObject("rss", Rss.class);
// System.out.println("WordPress blog rss data:" + rss);
Converter<Rss, List<PostVO>> converter = new WordPressConverter();
List<PostVO> postVoList = converter.convertFrom(rss);
System.out.println(postVoList);
}
private JSONObject readXml() throws IOException, URISyntaxException {
URL migrationUrl = ResourceUtils.getURL(ResourceUtils.CLASSPATH_URL_PREFIX + "wordpressdemo.xml");
URI wordpressUrl = migrationUrl.toURI();
File file = new File(wordpressUrl);
FileInputStream inputStream = new FileInputStream(file);
String s = XmlMigrateUtils.xml2jsonString(inputStream);
return JSONObject.parseObject(s);
}
}

View File

@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0"
xmlns:excerpt="http://wordpress.org/export/1.2/excerpt/"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:wp="http://wordpress.org/export/1.2/"
>
<channel>
<title>WordPress Demo</title>
<link>http://148.70.10.235</link>
<description>又一个WordPress站点</description>
<pubDate>Mon, 08 Jul 2019 07:27:10 +0000</pubDate>
<language>zh-CN</language>
<wp:wxr_version>1.2</wp:wxr_version>
<wp:base_site_url>http://148.70.10.235</wp:base_site_url>
<wp:base_bloge_url>http://148.70.10.235</wp:base_bloge_url>
<wp:author>
<wp:author_id>1</wp:author_id>
<wp:author_login><![CDATA[admin]]></wp:author_login>
<wp:author_email><![CDATA[i@ryanc.cc]]></wp:author_email>
<wp:author_display_name><![CDATA[admin]]></wp:author_display_name>
<wp:author_first_name><![CDATA[]]></wp:author_first_name>
<wp:author_last_name><![CDATA[]]></wp:author_last_name>
</wp:author>
<generator>https://wordpress.org/?v=4.8</generator>
<item>
<title>世界,您好!</title>
<link>http://148.70.10.235/index.php/2019/07/08/hello-world/</link>
<pubDate>Mon, 08 Jul 2019 03:10:55 +0000</pubDate>
<dc:creator><![CDATA[admin]]></dc:creator>
<guid isPermaLink="false">http://148.70.10.235/?p=1</guid>
<description>123123131</description>
<content:encoded><![CDATA[欢迎使用WordPress。这是您的第一篇文章。编辑或删除它然后开始写作吧
<h2>hello二级标题</h2>
<ul>
<li>list1</li>
<li>list2</li>
<li>list3</li>
</ul>
<h3>hello 三级标题</h3>
&nbsp;
<blockquote>daidjoadoiajdoijo这是引用
&nbsp;</blockquote>]]></content:encoded>
<excerpt:encoded><![CDATA[]]></excerpt:encoded>
<wp:post_id>1</wp:post_id>
<wp:post_date><![CDATA[2019-07-08 11:10:55]]></wp:post_date>
<wp:post_date_gmt><![CDATA[2019-07-08 03:10:55]]></wp:post_date_gmt>
<wp:comment_status><![CDATA[open]]></wp:comment_status>
<wp:ping_status><![CDATA[open]]></wp:ping_status>
<wp:post_name><![CDATA[hello-world]]></wp:post_name>
<wp:status><![CDATA[publish]]></wp:status>
<wp:post_parent>0</wp:post_parent>
<wp:menu_order>0</wp:menu_order>
<wp:post_type><![CDATA[post]]></wp:post_type>
<wp:post_password><![CDATA[]]></wp:post_password>
<wp:is_sticky>0</wp:is_sticky>
<category domain="post_format" nicename="post-format-aside"><![CDATA[日志]]></category>
<category domain="post_tag" nicename="%e6%a0%87%e7%ad%be1"><![CDATA[标签1]]></category>
<category domain="post_tag" nicename="%e6%a0%87%e7%ad%be2"><![CDATA[标签2]]></category>
<category domain="category" nicename="%e8%bf%99%e6%98%af%e5%88%86%e7%b1%bb"><![CDATA[这是分类]]></category>
<wp:postmeta>
<wp:meta_key><![CDATA[_edit_last]]></wp:meta_key>
<wp:meta_value><![CDATA[1]]></wp:meta_value>
</wp:postmeta>
<wp:postmeta>
<wp:meta_key><![CDATA[_thumbnail_id]]></wp:meta_key>
<wp:meta_value><![CDATA[9]]></wp:meta_value>
</wp:postmeta>
<wp:comment>
<wp:comment_id>1</wp:comment_id>
<wp:comment_author><![CDATA[一位WordPress评论者]]></wp:comment_author>
<wp:comment_author_email><![CDATA[wapuu@wordpress.example]]></wp:comment_author_email>
<wp:comment_author_url>https://wordpress.org/</wp:comment_author_url>
<wp:comment_author_IP><![CDATA[]]></wp:comment_author_IP>
<wp:comment_date><![CDATA[2019-07-08 11:10:55]]></wp:comment_date>
<wp:comment_date_gmt><![CDATA[2019-07-08 03:10:55]]></wp:comment_date_gmt>
<wp:comment_content><![CDATA[嗨,这是一条评论。
要开始审核、编辑及删除评论,请访问仪表盘的“评论”页面。
评论者头像来自<a href="https://gravatar.com">Gravatar</a>。]]></wp:comment_content>
<wp:comment_approved><![CDATA[1]]></wp:comment_approved>
<wp:comment_type><![CDATA[]]></wp:comment_type>
<wp:comment_parent>0</wp:comment_parent>
<wp:comment_user_id>0</wp:comment_user_id>
</wp:comment>
<wp:comment>
<wp:comment_id>2</wp:comment_id>
<wp:comment_author><![CDATA[admin]]></wp:comment_author>
<wp:comment_author_email><![CDATA[i@ryanc.cc]]></wp:comment_author_email>
<wp:comment_author_url></wp:comment_author_url>
<wp:comment_author_IP><![CDATA[60.160.204.183]]></wp:comment_author_IP>
<wp:comment_date><![CDATA[2019-07-08 15:26:57]]></wp:comment_date>
<wp:comment_date_gmt><![CDATA[2019-07-08 07:26:57]]></wp:comment_date_gmt>
<wp:comment_content><![CDATA[这是评论回复]]></wp:comment_content>
<wp:comment_approved><![CDATA[1]]></wp:comment_approved>
<wp:comment_type><![CDATA[]]></wp:comment_type>
<wp:comment_parent>1</wp:comment_parent>
<wp:comment_user_id>1</wp:comment_user_id>
</wp:comment>
</item>
<item>
<title>这是文章2222</title>
<link>http://148.70.10.235/index.php/2019/07/08/%e8%bf%99%e6%98%af%e6%96%87%e7%ab%a02222/</link>
<pubDate>Mon, 08 Jul 2019 07:20:18 +0000</pubDate>
<dc:creator><![CDATA[admin]]></dc:creator>
<guid isPermaLink="false">http://148.70.10.235/?p=6</guid>
<description>dadadadada</description>
<content:encoded><![CDATA[<h3>三级标题</h3>
<ul>
<li>列表</li>
<li>list2</li>
<li>list3</li>
</ul>
&nbsp;]]></content:encoded>
<excerpt:encoded><![CDATA[]]></excerpt:encoded>
<wp:post_id>6</wp:post_id>
<wp:post_date><![CDATA[2019-07-08 15:20:18]]></wp:post_date><!-- 要这个日期 -->
<wp:post_date_gmt><![CDATA[2019-07-08 07:20:18]]></wp:post_date_gmt>
<wp:comment_status><![CDATA[open]]></wp:comment_status>
<wp:ping_status><![CDATA[open]]></wp:ping_status>
<wp:post_name><![CDATA[%e8%bf%99%e6%98%af%e6%96%87%e7%ab%a02222]]></wp:post_name>
<wp:status><![CDATA[publish]]></wp:status>
<wp:post_parent>0</wp:post_parent>
<wp:menu_order>0</wp:menu_order>
<wp:post_type><![CDATA[post]]></wp:post_type>
<wp:post_password><![CDATA[]]></wp:post_password>
<wp:is_sticky>0</wp:is_sticky>
<category domain="post_tag" nicename="hello"><![CDATA[Hello]]></category>
<category domain="category" nicename="hello%e6%96%87%e7%ab%a0"><![CDATA[Hello文章]]></category>
<category domain="post_tag" nicename="java"><![CDATA[Java]]></category>
<category domain="post_tag" nicename="test"><![CDATA[Test]]></category>
<category domain="category" nicename="test%e6%96%87%e7%ab%a0"><![CDATA[Test文章]]></category>
<wp:postmeta>
<wp:meta_key><![CDATA[_edit_last]]></wp:meta_key>
<wp:meta_value><![CDATA[1]]></wp:meta_value>
</wp:postmeta>
</item>
</channel>
</rss>