使用 DSM(声明性流映射)将 XML 文件解组为对象

Unmarshall XML file to Object with DSM (Declarative Stream Mapping)

我需要将 XML 转换为 Java 对象。我为此选择了 Declarative Stream Mapping (DSM),但不清楚如何将具有属性的嵌套元素映射到列表中。具体如何在下一篇的YAML文件中描述:

<categories>
  <category id="1" parentId="2">Lorum Ipsum</category>
</categories>

我尝试了所有的选项,但都没有成功。

以下是我运行.

的代码片段

DSM(yaml):

  result:
  type: object
  path: /catalog/shop
  fields:
    name:
      path: name
    company:
      path: company
    url:
      path: url
    platform:
      path: platform
    agency:
      path: agency
    email:
      path: email
    currencies:
      type: array
      path: currencies/currency
      fields:
        id:
          xml:
            attribute: true
        rate:
          xml:
            attribute: true
    categories:
      type: array
      path: categories/category
      fields:
        id:
          xml:
            attribute: true
        parentId:
          xml:
            attribute: true
    deliveryOptions:
      type: array
      path: delivery-options/option
      fields:
        cost:
          xml:
            attribute: true
        days:
          xml:
            attribute: true
    offers:
      type: array
      path: offers/offer
      fields:
        id:
          xml:
            attribute: true
        available:
          xml:
            attribute: true
        type:
          xml:
            attribute: true
        param:
          path: param
        offer:
          fields:
            url:
              path: url
            price:
              path: price
# shortened due to length

XML 要转换的文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE yml_catalog SYSTEM "shops.dtd">
    <catalog date="2019-09-08 22:37">
      <shop>
        <name>company name</name>
        <company>Microsoft</company>
        <url>https://microsoft.com</url>
        <platform>woefjwoefi</platform>
        <agency>Agent</agency>
        <email>billi@ms.com</email>
        <currencies>
          <currency id="USD" rate="1"/>
        </currencies>
        <categories>
          <category id="398" parentId="198">Lorum Ipsum</category>
          <category id="409">Uncategorized</category>
        </categories>
        <offers>
          <offer id="2016" available="true" type="vendor.model">
            <url>https://ms.com/id/1</url>
            <price>35.00</price>
            <currencyId>USD</currencyId>
            <categoryId>168</categoryId>
            <picture>https://ms.com/image/cache/catalog/8987-600x600.jpg</picture>
            <store>true</store>
            <pickup>true</pickup>
            <delivery>true</delivery>
            <vendor>MS</vendor>
            <vendorCode>MS-10111</vendorCode>
            <model>plasdf</model>
            <downloadable>false</downloadable>
            <param name="Weight">0.0</param>
          </offer>
          <offer id="2017" available="true" type="vendor.model">
            <url>https://ms.com/id/2</url>
            <price>250.00</price>
            <currencyId>USD</currencyId>
            <categoryId>201</categoryId>
            <picture>https://ms.com/image/cache/catalog/aktiv-600x600.png</picture>
            <store>true</store>
            <pickup>true</pickup>
            <delivery>true</delivery>
            <vendor>MS</vendor>
            <vendorCode>MS03238</vendorCode>
            <model>woeifjwoef wefw fw we</model>
            <description>wiefwof wef</description>
            <downloadable>false</downloadable>
            <weight>30.0</weight>
            <param name="Weight">30.0</param>
          </offer>
        </offers>
      </shop>
    </catalog>

Java:

public class Shop {
  private String name;
  private String company;
  private String url;
  private String platform;
  private String agency;
  private String email;
  private List<Currencies> currencies;
  private List<Categories> categories;
  private List<DeliveryOptions> deliveryOptions;
  private List<Offers> offers;
  // getters/setters
}

class Currencies {
  // getters/setters
}

class Categories {
  // getters/setters
}

class DeliveryOptions {
  // getters/setters
}

class Offers {
  // getters/setters
}

输出:

{
  "name" : "company name",
  "company" : "Microsoft",
  "url" : "https://microsoft.com",
  "platform" : "woefjwoefi",
  "agency" : "Agent",
  "email" : "billi@ms.com",
  "currencies" : null,
  "categories": null,
  "deliveryOptions": null,
  "offers": null
}

我做了一些轻微的缩进调整,你的 dsm 配置文件工作得很好。

DSM.yml

result:
  type: object
  path: /catalog/shop
  fields:
    name:
      path: name
    company:
      path: company
    url:
      path: url
    platform:
      path: platform
    agency:
      path: agency
    email:
      path: email
    currencies:
      type: array
      path: currencies/currency
      fields:
        id:
          xml:
            attribute: true
        rate:
          xml:
            attribute: true
    categories:
      type: array
      path: categories
      xml:
        path: categories/category
      fields:
        name:
          path: ./
        id:
          xml:
            attribute: true
        parentId:
          xml:
            attribute: true
    deliveryOptions:
      type: array
      path: delivery-options/option
      fields:
        cost:
          xml:
            attribute: true
        days:
          xml:
            attribute: true
    offers:
      type: array
      path: offers/offer
      fields:
        id:
          xml:
            attribute: true
        available:
          xml:
            attribute: true
        type:
          xml:
            attribute: true
        param:
          path: param
        url: default
        price: default
        currencyId: default
        categoryId: default

输出:

{
  "name" : "company name",
  "company" : "Microsoft",
  "url" : "https://microsoft.com",
  "platform" : "woefjwoefi",
  "agency" : "Agent",
  "email" : "billi@ms.com",
  "currencies" : [ {
    "id" : "USD",
    "rate" : 1
  } ],
  "categories" : [ {
    "id" : 398,
    "parentId" : 198,
    "name" : "Lorum Ipsum"
  }, {
    "id" : 409,
    "parentId" : null,
    "name" : "Uncategorized"
  } ],
  "deliveryOptions" : null,
  "offers" : [ {
    "id" : "2016",
    "available" : true,
    "type" : "vendor.model",
    "url" : "https://ms.com/id/1",
    "price" : 35.0,
    "currencyId" : "USD",
    "categoryId" : "168",
  }, {
    "id" : "2017",
    "available" : true,
    "type" : "vendor.model",
    "url" : "https://ms.com/id/2",
    "price" : 250.0,
    "currencyId" : "USD",
    "categoryId" : "201",

  } ]
}

这是我用来测试它的代码:

@Test
public void dsmParsing() throws IOException {

    InputStream is = new FileInputStream("src/main/resources/dsm.yml");

    DSMBuilder builder = new DSMBuilder(is);
    DSM dsm = builder.setType(DSMBuilder.TYPE.XML).create(Shop.class);
    Shop shop = (Shop) dsm.toObject(new File("src/main/resources/shop.xml"));

    ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();

    System.out.println(ow.writeValueAsString(shop));
}

要回答有关带有属性的嵌套元素的问题,您必须考虑到当您有此定义时:

categories:
  type: array
  path: categories/category

当您定义 fields 时,您将描述元素 <category>

在下面的示例中,属性 idparentId 将在 fields 段中定义为属性,但要获取实际 category 元素的值,您需要使用 ./ 引用当前路径。如果您有子元素,则只需在 fields 段中添加这些子元素。

xml

 <categories>
   <category id="398" parentId="198">Lorum Ipsum</category>
 </categories>

dsm

categories:
  type: array
  path: categories/category
  fields:
    name: ## you could call it 'value' or whatever you want
      path: ./
    id:
      xml:
        attribute: true
    parentId:
      xml:
        attribute: true

下一个例子很有趣:

xml

<offers attr1="abc" attr2="def">
    <offer id="2016" available="true">
        <url>https://ms.com/id/1</url>
    </offer>
</offers>

首先,您需要另一个 pojo 来正确表示 offers 及其属性:

店铺

public class Shop {
  ...
  OffersList offers; // this attribute's name needs to match the one used in the dsm descriptor
}

新建class优惠列表

public class OffersList {
  String attr1;
  String attr2;
  List<Offers> offer; // this attribute's name needs to match the one used in the dsm descriptor
}

最终的 dsm 将如下所示:

offers: ## name should match with Shop's attribute 'offers' (OffersList)
  type: object ## now offers is an object, is mandatory to define 'fields'
  path: offers  ## focus is on the <offers> element
  fields:
    attr1:
      xml:
        attribute: true
    attr2:
      xml:
        attribute: true
    offer:   ## name should match with OffersList's attribute 'offer'
      type: array
      path: ../offers/offer  ## will go back a level to indicate <offers> <offer> should be parsed as an array
      fields:
        id:
          xml:
            attribute: true
        available:
          xml:
            attribute: true
        url:
          path: url

输出

  "offers" : {
    "attr1" : "abc",
    "attr2" : "def",
    "offer" : [ {
      "id" : "2016",
      "available" : true,
      "url" : "https://ms.com/id/1",
    } ]
  }