diff --git a/build.gradle b/build.gradle index aca1d71e4..7c545968e 100644 --- a/build.gradle +++ b/build.gradle @@ -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' diff --git a/src/main/java/run/halo/app/handler/migrate/WordPressMigrateHandler.java b/src/main/java/run/halo/app/handler/migrate/WordPressMigrateHandler.java index 2f38bdb6d..ee6ab7d09 100644 --- a/src/main/java/run/halo/app/handler/migrate/WordPressMigrateHandler.java +++ b/src/main/java/run/halo/app/handler/migrate/WordPressMigrateHandler.java @@ -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 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 categories = handleCategories(resultSetMapping.get("wp:category")); + // 转换 + Converter> converter = new WordPressConverter(); - // Handle tags - List tags = handleTags(resultSetMapping.get("wp:tag")); + List postVoList = converter.convertFrom(rss); - // Handle posts - List 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 handleCategories(@Nullable Object categoriesObject) { - - if (!(categoriesObject instanceof List)) { - return Collections.emptyList(); - } - - List categoryObjectList = (List) categoriesObject; - - List result = new LinkedList<>(); - - categoryObjectList.forEach(categoryObject -> { - - if (!(categoryObject instanceof Map)) { - return; - } - - Map categoryMap = (Map) 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 handleTags(@Nullable Object tagsObject) { - - if (!(tagsObject instanceof List)) { - return Collections.emptyList(); - } - - List tagObjectList = (List) tagsObject; - - List result = new LinkedList<>(); - - tagObjectList.forEach(tagObject -> { - if (!(tagObject instanceof Map)) { - return; - } - - Map tagMap = (Map) 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 handlePosts(@Nullable Object postsObject) { - if (!(postsObject instanceof List)) { - return Collections.emptyList(); - } - - List postObjectList = (List) postsObject; - - List result = new LinkedList<>(); - - postObjectList.forEach(postObject -> { - if (!(postObject instanceof Map)) { - return; - } - - Map postMap = (Map) 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); diff --git a/src/main/java/run/halo/app/handler/migrate/converter/Converter.java b/src/main/java/run/halo/app/handler/migrate/converter/Converter.java new file mode 100644 index 000000000..aa45f8ec0 --- /dev/null +++ b/src/main/java/run/halo/app/handler/migrate/converter/Converter.java @@ -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; + +/** + * 博客迁移数据转换器,只定义从SOURCE转TARGET的单向转换 + * + * @author guqing + * @date 2020-01-18 16:45 + */ +public interface Converter { + + /** + * 将source转换为target + * + * @param s 需要转换的源对象 + * @return 返回转换得到的结果对象 + */ + TARGET convertFrom(SOURCE s); + + /** + * 从SOURCE转为TARGET + * + * @param s source,需要转换的原始SOURCE集合 + * @param function 具体转换逻辑 + * @return 返回转换得到的TARGET对象 + */ + default TARGET convertFrom(SOURCE s, Function function) { + return function.apply(s); + } + + /** + * 批量从SOURCE转换得到TARGET + * + * @param list 需要转换的原始SOURCE集合 + * @param function 具体转换逻辑 + * @return 返回转换得到的TARGET集合结果 + */ + default List batchConverterFromDto(List list, Function function) { + return list.stream().map(s -> convertFrom(s, function)).collect(Collectors.toList()); + } +} + diff --git a/src/main/java/run/halo/app/handler/migrate/converter/WordPressConverter.java b/src/main/java/run/halo/app/handler/migrate/converter/WordPressConverter.java new file mode 100644 index 000000000..f4592095d --- /dev/null +++ b/src/main/java/run/halo/app/handler/migrate/converter/WordPressConverter.java @@ -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> { + + @Override + public List convertFrom(Rss rss) { + return convertFrom(rss, this::apply); + } + + /** + * 自定义转换规则 + */ + public List apply(Rss rss) { + if (Objects.isNull(rss) || Objects.isNull(rss.getChannel())) { + return new ArrayList<>(); + } + + Channel channel = rss.getChannel(); + List items = channel.getItems(); + + if (items == null) { + return new ArrayList<>(); + } + return getBasePost(items); + } + + private List getBasePost(List items) { + List posts = new ArrayList<>(); + if (items == null) { + return posts; + } + + for (Item item : items) { + PostVO postVo = new PostVO(); + // 设置文章 + BasePost post = getBasePostFromItem(item); + postVo.setBasePost(post); + + // 获取标签和分类 + List categories = item.getCategories(); + List categoryModelList = new ArrayList<>(); + List 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 comments = getCommentsFromItem(item); + postVo.setComments(comments); + + posts.add(postVo); + } + + return posts; + } + + private List getCommentsFromItem(Item item) { + List baseComments = new ArrayList<>(); + if (Objects.isNull(item) || Objects.isNull(item.getComments())) { + return baseComments; + } + + List 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; + } +} diff --git a/src/main/java/run/halo/app/handler/migrate/support/vo/PostVO.java b/src/main/java/run/halo/app/handler/migrate/support/vo/PostVO.java new file mode 100644 index 000000000..fa01ceb40 --- /dev/null +++ b/src/main/java/run/halo/app/handler/migrate/support/vo/PostVO.java @@ -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 tags; + private List categories; + private List comments; +} diff --git a/src/main/java/run/halo/app/handler/migrate/support/wordpress/Channel.java b/src/main/java/run/halo/app/handler/migrate/support/wordpress/Channel.java new file mode 100644 index 000000000..fc1d5f47f --- /dev/null +++ b/src/main/java/run/halo/app/handler/migrate/support/wordpress/Channel.java @@ -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; + +/** + *

+ * WordPress导出的xml中对应的channel节点下的子节点将被映射为该类的属性 + *

+ * + * @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 items; +} diff --git a/src/main/java/run/halo/app/handler/migrate/support/wordpress/Comment.java b/src/main/java/run/halo/app/handler/migrate/support/wordpress/Comment.java new file mode 100644 index 000000000..a325d9d7d --- /dev/null +++ b/src/main/java/run/halo/app/handler/migrate/support/wordpress/Comment.java @@ -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; + +/** + *

+ * WordPress导出的xml中对于的comment节点下的子节点值将被映射为该类的属性, + * 最终会被转换为{@link run.halo.app.model.entity.PostComment} + *

+ * + * @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; +} diff --git a/src/main/java/run/halo/app/handler/migrate/support/wordpress/Item.java b/src/main/java/run/halo/app/handler/migrate/support/wordpress/Item.java new file mode 100644 index 000000000..779a4c9c5 --- /dev/null +++ b/src/main/java/run/halo/app/handler/migrate/support/wordpress/Item.java @@ -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; + +/** + *

WordPress导出的xml中对于的item子节点的值将会被映射到该类的属性上,最终被解析为文章属性{@link BasePost}

+ * + * @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 comments; + + @JSONField(name = "category") + private List categories; +} diff --git a/src/main/java/run/halo/app/handler/migrate/support/wordpress/Rss.java b/src/main/java/run/halo/app/handler/migrate/support/wordpress/Rss.java new file mode 100644 index 000000000..c1f84a5f7 --- /dev/null +++ b/src/main/java/run/halo/app/handler/migrate/support/wordpress/Rss.java @@ -0,0 +1,15 @@ +package run.halo.app.handler.migrate.support.wordpress; + +import lombok.Data; + +/** + * WordPress导出的xml数据中对应的rss节点下的子节点channel将会被映射到该类属性 + * 如果不写这个Rss类包装想要的Channel,会无法解析到想要的结果 + * + * @author guqing + * @date 2020-01-17 00:45 + */ +@Data +public class Rss { + private Channel channel; +} diff --git a/src/main/java/run/halo/app/handler/migrate/support/wordpress/WpCategory.java b/src/main/java/run/halo/app/handler/migrate/support/wordpress/WpCategory.java new file mode 100644 index 000000000..e020c318a --- /dev/null +++ b/src/main/java/run/halo/app/handler/migrate/support/wordpress/WpCategory.java @@ -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; + +/** + * WordPress导出的xml数据中对应的category的子节点将会被映射到该类属性, + * 最终会被转换为文章分类{@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; +} diff --git a/src/main/java/run/halo/app/handler/migrate/utils/PropertyMappingTo.java b/src/main/java/run/halo/app/handler/migrate/utils/PropertyMappingTo.java new file mode 100644 index 000000000..3fd4e78e8 --- /dev/null +++ b/src/main/java/run/halo/app/handler/migrate/utils/PropertyMappingTo.java @@ -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 ""; +} diff --git a/src/main/java/run/halo/app/handler/migrate/utils/RelationMapperUtils.java b/src/main/java/run/halo/app/handler/migrate/utils/RelationMapperUtils.java new file mode 100644 index 000000000..a5c799dd3 --- /dev/null +++ b/src/main/java/run/halo/app/handler/migrate/utils/RelationMapperUtils.java @@ -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 TARGET convertFrom(SOURCE source, Class targetClazz) { + Map 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 TARGET propertyMapper(SOURCE source, TARGET target, Map 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 void copyProperty(String sourceFieldName, String targetFieldName, + SOURCE source, TARGET target, Class sourceType, + Map 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("转换失败,请检查属性类型是否匹配"); + } + } + + /** + * 通过属性名称得到对于的get或set方法 + * + * @param fieldName 属性名称 + * @param methodType 方法类型可选值为:get或set + * @return 返回对象的get或set方法的名称 + */ + 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"); + } +} diff --git a/src/main/java/run/halo/app/utils/WordPressMigrateUtils.java b/src/main/java/run/halo/app/utils/WordPressMigrateUtils.java index dea2ec6a9..f9ed2b7e2 100644 --- a/src/main/java/run/halo/app/utils/WordPressMigrateUtils.java +++ b/src/main/java/run/halo/app/utils/WordPressMigrateUtils.java @@ -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存储 */ diff --git a/src/main/java/run/halo/app/utils/XmlMigrateUtils.java b/src/main/java/run/halo/app/utils/XmlMigrateUtils.java new file mode 100644 index 000000000..af70e749b --- /dev/null +++ b/src/main/java/run/halo/app/utils/XmlMigrateUtils.java @@ -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; + +/** + *

将xml解析为json

+ * + * @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(); + } +} \ No newline at end of file diff --git a/src/test/java/run/halo/app/utils/XmlMigrateUtilsTest.java b/src/test/java/run/halo/app/utils/XmlMigrateUtilsTest.java new file mode 100644 index 000000000..c8ae255a7 --- /dev/null +++ b/src/test/java/run/halo/app/utils/XmlMigrateUtilsTest.java @@ -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> converter = new WordPressConverter(); + List 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); + } +} diff --git a/src/test/resources/wordpressdemo.xml b/src/test/resources/wordpressdemo.xml new file mode 100644 index 000000000..ec965732e --- /dev/null +++ b/src/test/resources/wordpressdemo.xml @@ -0,0 +1,144 @@ + + + + WordPress Demo + http://148.70.10.235 + 又一个WordPress站点 + Mon, 08 Jul 2019 07:27:10 +0000 + zh-CN + 1.2 + http://148.70.10.235 + http://148.70.10.235 + + + 1 + + + + + + + + + https://wordpress.org/?v=4.8 + + + 世界,您好! + http://148.70.10.235/index.php/2019/07/08/hello-world/ + Mon, 08 Jul 2019 03:10:55 +0000 + + http://148.70.10.235/?p=1 + 123123131 + hello二级标题 +
    +
  • list1
  • +
  • list2
  • +
  • list3
  • +
+

hello 三级标题

+  +
daidjoadoiajdoijo这是引用 + + 
]]>
+ + 1 + + + + + + + 0 + 0 + + + 0 + + + + + + + + + + + + + + 1 + + + https://wordpress.org/ + + + + Gravatar。]]> + + + 0 + 0 + + + 2 + + + + + + + + + + 1 + 1 + +
+ + 这是文章2222 + http://148.70.10.235/index.php/2019/07/08/%e8%bf%99%e6%98%af%e6%96%87%e7%ab%a02222/ + Mon, 08 Jul 2019 07:20:18 +0000 + + http://148.70.10.235/?p=6 + dadadadada + 三级标题 +
    +
  • 列表
  • +
  • list2
  • +
  • list3
  • +
+ ]]>
+ + 6 + + + + + + + 0 + 0 + + + 0 + + + + + + + + + +
+
+