加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

一个略复杂的数据映射聚合例子及代码重构(下)

发布时间:2020-12-14 05:06:20 所属栏目:百科 来源:网络整理
导读:背景与问题 在 《一个略复杂的数据映射聚合例子及代码重构》 一文中,将一个JSON字符串转成了所需要的订单信息Map。尽管做了代码重构和配置化,过程式的代码仍然显得晦涩难懂,并且客户端使用Map也非常难受。 能不能把这个JSON串转成相应的对象,更易于使用

背景与问题

在 《一个略复杂的数据映射聚合例子及代码重构》 一文中,将一个JSON字符串转成了所需要的订单信息Map。尽管做了代码重构和配置化,过程式的代码仍然显得晦涩难懂,并且客户端使用Map也非常难受。

能不能把这个JSON串转成相应的对象,更易于使用呢? 为了方便讲解,这里重复写下JSON串。

{
    "item:s_id:18006666": "1024","item:s_id:18008888": "1024","item:g_id:18006666": "6666","item:g_id:18008888": "8888","item:num:18008888": "8","item:num:18006666": "6","item:item_core_id:18006666": "9876666","item:item_core_id:18008888": "9878888","item:order_no:18006666": "E20171013174712025","item:order_no:18008888": "E20171013174712025","item:id:18008888": "18008888","item:id:18006666": "18006666","item_core:num:9878888": "8","item_core:num:9876666": "6","item_core:id:9876666": "9876666","item_core:id:9878888": "9878888","item_price:item_id:1000": "9876666","item_price:item_id:2000": "9878888","item_price:price:1000": "100","item_price:price:2000": "200","item_price:id:2000": "2000","item_price:id:1000": "1000","item_price_change_log:id:1111": "1111","item_price_change_log:id:2222": "2222","item_price_change_log:item_id:1111": "9876666","item_price_change_log:item_id:2222": "9878888","item_price_change_log:detail:1111": "haha1111","item_price_change_log:detail:2222": "haha2222","item_price_change_log:id:3333": "3333","item_price_change_log:id:4444": "4444","item_price_change_log:item_id:3333": "9876666","item_price_change_log:item_id:4444": "9878888","item_price_change_log:detail:3333": "haha3333","item_price_change_log:detail:4444": "haha4444"
}


思路与实现

要解决这个问题,需要有一个清晰的思路。

  • 首先,需要知道应该转成怎样的目标对象。
  • 其次,需要找到一种方法,建立从JSON串到目标对象的桥梁。

推断目标对象

仔细观察可知,每个 key 都是 tablename:field:id 组成,其中 table:id 相同的可以构成一个对象的数据; 此外,不同的tablename 对应不同的对象,而这些对象之间可以通过相同的 itemId 关联。

根据对JSON字符串的仔细分析(尤其是字段的关联性),可以知道: 目标对象应该类似如下嵌套对象:

@Getter
@Setter
public class ItemCore {
  private String id;
  private String num;

  private Item item;

  private ItemPrice itemPrice;

  private List<ItemPriceChangeLog> itemPriceChangeLogs;

}


@Getter
@Setter
public class Item {
  private String sId;
  private String gId;
  private String num;
  private String orderNo;
  private String id;
  private String itemCoreId;

}

@Getter
@Setter
public class ItemPrice {
  private String itemId;
  private String price;
  private String id;
}

@Getter
@Setter
public class ItemPriceChangeLog {
  private String id;
  private String itemId;
  private String detail;
}

注意到,对象里的属性是驼峰式,JSON串里的字段是下划线,遵循各自领域内的命名惯例。这里需要用到一个函数,将Map的key从下划线转成驼峰。这个方法在 《Java实现递归将嵌套Map里的字段名由驼峰转为下划线》 给出。

明确了目标对象,就成功了 30%。 接下来,需要找到一种方法,从指定字符串转换到这个对象。

算法设计

由于 JSON 并不是与对象结构对应的嵌套结构。需要先转成容易处理的Map对象。这里的一种思路是,

STEP1: 将 table:id 相同的字段及值分组聚合,得到 Map[tablename:id,mapForKey[field,value]];

STEP2: 将每个 mapForKey[field,value] 转成 tablename 对应的单个对象 Item,ItemCore,ItemPrice,ItemPriceChangeLog;

STEP3: 然后根据 itemId 来关联这些对象,组成最终对象。

代码实现

package zzz.study.algorithm.object;

import com.alibaba.fastjson.JSON;

import org.apache.commons.beanutils.BeanUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import zzz.study.datastructure.map.TransferUtil;

public class MapToObject {

  private static final String json = "{n"
             + "    "item:s_id:18006666": "1024",n"
             + "    "item:s_id:18008888": "1024",n"
             + "    "item:g_id:18006666": "6666",n"
             + "    "item:g_id:18008888": "8888",n"
             + "    "item:num:18008888": "8",n"
             + "    "item:num:18006666": "6",n"
             + "    "item:item_core_id:18006666": "9876666",n"
             + "    "item:item_core_id:18008888": "9878888",n"
             + "    "item:order_no:18006666": "E20171013174712025",n"
             + "    "item:order_no:18008888": "E20171013174712025",n"
             + "    "item:id:18008888": "18008888",n"
             + "    "item:id:18006666": "18006666",n"
             + "    n"
             + "    "item_core:num:9878888": "8",n"
             + "    "item_core:num:9876666": "6",n"
             + "    "item_core:id:9876666": "9876666",n"
             + "    "item_core:id:9878888": "9878888",n"
             + "n"
             + "    "item_price:item_id:1000": "9876666",n"
             + "    "item_price:item_id:2000": "9878888",n"
             + "    "item_price:price:1000": "100",n"
             + "    "item_price:price:2000": "200",n"
             + "    "item_price:id:2000": "2000",n"
             + "    "item_price:id:1000": "1000",n"
             + "n"
             + "    "item_price_change_log:id:1111": "1111",n"
             + "    "item_price_change_log:id:2222": "2222",n"
             + "    "item_price_change_log:item_id:1111": "9876666",n"
             + "    "item_price_change_log:item_id:2222": "9878888",n"
             + "    "item_price_change_log:detail:1111": "haha1111",n"
             + "    "item_price_change_log:detail:2222": "haha2222",n"
             + "    "item_price_change_log:id:3333": "3333",n"
             + "    "item_price_change_log:id:4444": "4444",n"
             + "    "item_price_change_log:item_id:3333": "9876666",n"
             + "    "item_price_change_log:item_id:4444": "9878888",n"
             + "    "item_price_change_log:detail:3333": "haha3333",n"
             + "    "item_price_change_log:detail:4444": "haha4444"n"
             + "}";


  public static void main(String[] args) {
    Order order = transferOrder(json);
    System.out.println(JSON.toJSONString(order));
  }

  public static Order transferOrder(String json) {
    return relate(underline2camelForMap(group(json)));
  }

  /**
   * 转换成 Map[tablename:id => Map["field": value]]
   */
  public static Map<String,Map<String,Object>> group(String json) {
    Map<String,Object> map = JSON.parSEObject(json);
    Map<String,Object>> groupedMaps = new HashMap();
    map.forEach(
        (keyInJson,value) -> {
          TableField tableField = TableField.buildFrom(keyInJson);
          String key = tableField.getTablename() + ":" + tableField.getId();
          Map<String,Object> mapForKey = groupedMaps.getOrDefault(key,new HashMap<>());
          mapForKey.put(tableField.getField(),value);
          groupedMaps.put(key,mapForKey);
        }
    );
    return groupedMaps;
  }

  public static Map<String,Object>> underline2camelForMap(Map<String,Object>> underlined) {
    Map<String,Object>> groupedMapsCamel = new HashMap<>();
    Set<String> ignoreSets = new HashSet();
    underlined.forEach(
        (key,mapForKey) -> {
          Map<String,Object> keytoCamel = TransferUtil.generalMapProcess(mapForKey,TransferUtil::underlineToCamel,ignoreSets);
          groupedMapsCamel.put(key,keytoCamel);
        }
    );
    return groupedMapsCamel;
  }

  /**
   * 将分组后的子map先转成相应单个对象,再按照某个key值进行关联
   */
  public static Order relate(Map<String,Object>> groupedMaps) {
    List<Item> items = new ArrayList<>();
    List<ItemCore> itemCores = new ArrayList<>();
    List<ItemPrice> itemPrices = new ArrayList<>();
    List<ItemPriceChangeLog> itemPriceChangeLogs = new ArrayList<>();
    groupedMaps.forEach(
        (key,mapForKey) -> {
          if (key.startsWith("item:")) {
            items.add(map2Bean(mapForKey,Item.class));
          }
          else if (key.startsWith("item_core:")) {
            itemCores.add(map2Bean(mapForKey,ItemCore.class));
          }
          else if (key.startsWith("item_price:")) {
            itemPrices.add(map2Bean(mapForKey,ItemPrice.class));
          }
          else if (key.startsWith("item_price_change_log:")) {
            itemPriceChangeLogs.add(map2Bean(mapForKey,ItemPriceChangeLog.class));
          }
        }
    );

    Map<String,List<Item>> itemMap = items.stream().collect(Collectors.groupingBy(
        Item::getItemCoreId
    ));
    Map<String,List<ItemPrice>> itemPriceMap = itemPrices.stream().collect(Collectors.groupingBy(
        ItemPrice::getItemId
    ));
    Map<String,List<ItemPriceChangeLog>> itemPriceChangeLogMap = itemPriceChangeLogs.stream().collect(Collectors.groupingBy(
        ItemPriceChangeLog::getItemId
    ));
    itemCores.forEach(
        itemCore -> {
          String itemId = itemCore.getId();
          itemCore.setItem(itemMap.get(itemId).get(0));
          itemCore.setItemPrice(itemPriceMap.get(itemId).get(0));
          itemCore.setItemPriceChangeLogs(itemPriceChangeLogMap.get(itemId));
        }
    );
    Order order = new Order();
    order.setItemCores(itemCores);
    return order;
  }

  public static <T> T map2Bean(Map map,Class<T> c) {
    try {
      T t = c.newInstance();
      BeanUtils.populate(t,map);
      return t;
    } catch (Exception ex) {
      throw new RuntimeException(ex.getCause());
    }
  }

}
@Data
public class TableField {

  String tablename;
  String field;
  String id;

  public TableField(String tablename,String field,String id) {
    this.tablename = tablename;
    this.field = field;
    this.id = id;
  }

  public static TableField buildFrom(String combined) {
    String[] parts = combined.split(":");
    if (parts != null && parts.length == 3) {
      return new TableField(parts[0],parts[1],parts[2]);
    }
    throw new IllegalArgumentException(combined);
  }

}


小结

本文展示了一种方法, 将具有内在关联性的JSON字符串转成对应的嵌套对象。 当处理复杂业务关联的数据时,相比过程式的思维,转换为对象的视角会更容易处理和使用。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读