在POJO内部使用JAXB映射为XML的地图。

huangapple go评论63阅读模式
英文:

Map inside POJO to XML with JAXB

问题

我有一个像这样的产品POJO:

@XmlRootElement(name = "o")
@XmlAccessorType(XmlAccessType.FIELD)
public class Product {
    @XmlAttribute
    private Integer avail;
    @XmlAttribute
    private Long id;
    @XmlAttribute
    private BigDecimal price;
    @XmlAttribute
    private String url;
    @XmlAttribute
    private Integer stock;
    private String category;
    private String title;
    private String description;
    @XmlElementWrapper(name = "attrs")
    @XmlElement(name = "a")
    private Map<String, String> attributes;

    //getters, setters, toString
}

下一个类给我产品列表。

@XmlRootElement(name = "offers")
@XmlAccessorType(XmlAccessType.FIELD)
public class Products {

    @XmlElement(name = "o")
    private List<Product> products = null;

    public List<Product> getProducts() {
        return products;
    }

    public void setProducts(List<Product> products) {
        this.products = products;
    }
}

下一个类从数据库获取数据并将其作为产品列表返回:

private Connection conn = null;
private ResultSet rs = null;
private PreparedStatement selectProducts = null;
private Query query = new Query();
private Map<String, String> attr = new HashMap<String , String>();
private List<Product> mainList = new ArrayList<>();
private DescCreator descCreator = new DescCreator(); //taking care of product html description

public List<Product> listFiller() {
    try {
        conn = DriverManager.getConnection(connectionData);
        selectProducts = conn.prepareCall(query.getMainQuery());
        rs = selectProducts.executeQuery();

        while(rs.next()){
            Product product = new Product();
            product.setId(rs.getLong("products_id"));
            product.setPrice(rs.getBigDecimal("products_price_tax"));
            product.setUrl("https://www.link.com/" + rs.getString("products_id") + ".html");
            product.setAvail(rs.getInt("products_availability_id"));
            product.setStock(rs.getInt("products_quantity"));
            product.setCategory(rs.getString("categories_name"));
            product.setTitle(rs.getString("products_name"));
            product.setDescription(descCreator.descript(rs.getString("products_description")));
            attr.put(rs.getString("products_extra_fields_name"), rs.getString("products_extra_fields_value"));
            product.setAttributes(attr);
            mainList.add(product);
        }

    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }
    return mainList;
}

用于填充主列表的类(我的列表是从其他较小的列表构建的)。

@XmlRootElement(name = "offers")
@XmlAccessorType(XmlAccessType.PROPERTY)
public class ListInitializer {
    //从其他较小的列表创建主列表的代码
    return mainList;
}

以及主要的 marshal 类:

public class MainMarshal {
    private ListInitializer mainListInitializer = new ListInitializer();
    private Products products = new Products();

    public void marshalMain() throws JAXBException, IOException {
        products.setProducts(mainListInitializer.generateMainXml());

        JAXBContext jaxbContext = JAXBContext.newInstance(Products.class);
        Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

        jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        jaxbMarshaller.marshal(products, new File("filepath"));
    }
}

我的代码的结果类似于这样:

<offers>
  <o avail="1" id="1234" price="1234.00" url="some url" stock="1234">
    <category>PC</category>
    <title>Some product title</title>
    <desc>Some product description</desc>
    <attrs>
      <entry>
        <key>Map key</key>
        <value>Map value</value>
      </entry>
      <entry>
        <key>Map key</key>
        <value>Map value</value>
      </entry>
      <entry>
        <key>Map key</key>
        <value>Map value</value>
      </entry>
    </attrs>
  </o>
</offers>

我需要的结果是这样的:

<offers>
  <o avail="1" id="1234" price="1234.00" url="some url" stock="1234">
    <category>PC</category>
    <title>Some product title</title>
    <desc>Some product description</desc>
    <attrs>
      <a name="Map key">Map value</a>
      <a name="Map key">Map value</a>
      <a name="Map key">Map value</a>
      <a name="Map key">Map value</a>
    </attrs>
  </o>
</offers>

有关如何实现这个结果的建议或指南吗?是否有改变标签名称(entrysetvalue)的方法?

英文:

I have POJO of product like this:

@XmlRootElement(name = &quot;o&quot;)
@XmlAccessorType(XmlAccessType.FIELD)
public class Product {
@XmlAttribute
private Integer avail;
@XmlAttribute
private  Long id;
@XmlAttribute
private BigDecimal price;
@XmlAttribute
private String url;
@XmlAttribute
private Integer stock;
private String category;
private String title;
private String description;
@XmlElementWrapper(name = &quot;attrs&quot;)
@XmlElement(name = &quot;a&quot;)
private Map&lt;String, String&gt; attributes;
//getters, setters, toString
}

Next class gives me list of products.

@XmlRootElement(name = &quot;offers&quot;)
@XmlAccessorType(XmlAccessType.FIELD)
public class Products {
@XmlElement(name = &quot;o&quot;)
private List&lt;Product&gt; products = null;
public List&lt;Product&gt; getProducts() {
return products;
}
public void setProducts(List&lt;Product&gt; products) {
this.products = products;
}
}

Next class gets data from database and return it as a product list:

    private Connection conn = null;
private ResultSet rs = null;
private PreparedStatement selectProducts = null;
private Query query = new Query();
private Map&lt;String, String&gt; attr = new HashMap&lt;String , String&gt;();
private List&lt;Product&gt; mainList = new ArrayList&lt;&gt;();
private DescCreator descCreator = new DescCreator(); //taking care of product html description
public List&lt;Product&gt; listFiller() {
try {
conn = DriverManager.getConnection(connectionData);
selectProducts = conn.prepareCall(query.getMainQuery());
rs = selectProducts.executeQuery();
while(rs.next()){
Product product = new Product();
product.setId(rs.getLong(&quot;products_id&quot;));
product.setPrice(rs.getBigDecimal(&quot;products_price_tax&quot;));
product.setUrl(&quot;https://www.link.com/&quot; + rs.getString(&quot;products_id&quot;) + &quot;.html&quot;);
product.setAvail(rs.getInt(&quot;products_availability_id&quot;));
product.setStock(rs.getInt(&quot;products_quantity&quot;));
product.setCategory(rs.getString(&quot;categories_name&quot;));
product.setTitle(rs.getString(&quot;products_name&quot;));
product.setDescription(descCreator.descript(rs.getString(&quot;products_description&quot;)));
attr.put(rs.getString(&quot;products_extra_fields_name&quot;), rs.getString(&quot;products_extra_fields_value&quot;));
product.setAttributes(attr);
mainList.add(product);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return mainList;
}

Class to populate main list (my list was built from other smaller lists).

@XmlRootElement(name = &quot;offers&quot;)
@XmlAccessorType(XmlAccessType.PROPERTY)
public class listInitializer {
//code to create main list from other smaller list
return mainList;
}
}

And main marshal class:

public class MainMarshal{
private MainListInitializer mainListInitializer = new MainListInitializer();
private Products products = new Products();
public void marshalMain() throws JAXBException, IOException {
products.setProducts(mainListInitializer.generateMainXml());
JAXBContext jaxbContext = JAXBContext.newInstance(Products.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(products, new File(&quot;filepath&quot;));
}
}

Result of my code is something like this:

&lt;offers&gt;
&lt;o avail=&quot;1&quot; id=&quot;1234&quot; price=&quot;1234.00&quot; url=&quot;some url&quot; stock=&quot;1234&quot;&gt;
&lt;category&gt;PC&lt;/category&gt;
&lt;title&gt;Some product title&lt;/title&gt;
&lt;desc&gt;Some product description&lt;/desc&gt;
&lt;attrs&gt;
&lt;entry&gt;
&lt;key&gt;Map key&lt;/key&gt;
&lt;value&gt;Map value&lt;/value&gt;
&lt;/entry&gt;
&lt;entry&gt;
&lt;key&gt;Map key&lt;/key&gt;
&lt;value&gt;Map value&lt;/value&gt;
&lt;/entry&gt;
&lt;entry&gt;
&lt;key&gt;Map key&lt;/key&gt;
&lt;value&gt;Map value&lt;/value&gt;
&lt;/entry&gt;
&lt;/atrts&gt;
&lt;/o&gt;
&lt;/offers&gt;

And i need this as a result:

&lt;offers&gt;
&lt;o avail=&quot;1&quot; id=&quot;1234&quot; price=&quot;1234.00&quot; url=&quot;some url&quot; stock=&quot;1234&quot;&gt;
&lt;category&gt;PC&lt;/category&gt;
&lt;title&gt;Some product title&lt;/title&gt;
&lt;desc&gt;Some product description&lt;/desc&gt;
&lt;attrs&gt;
&lt;a name=&quot;Map key&quot;&gt;Map value&lt;/a&gt;
&lt;a name=&quot;Map key&quot;&gt;Map value&lt;/a&gt;
&lt;a name=&quot;Map key&quot;&gt;Map value&lt;/a&gt;
&lt;a name=&quot;Map key&quot;&gt;Map value&lt;/a&gt;
&lt;/atrts&gt;
&lt;/o&gt;
&lt;/offers&gt;

Any suggetions or guides how to achiev this result? Is there any way to change tag names: entry, set and value?

答案1

得分: 1

以下是翻译好的内容:

为此,您将需要使用XmlAdapter。

应创建以下bean:

public class Entry {

    @XmlAttribute
    public String name;
    
    @XmlValue
    public String value;

}

public class MapWrapper {

    @XmlElement(name="a")
    public List<Entry> entry = new ArrayList<>();

}

XML适配器:

import java.util.Map;
import java.util.TreeMap;
import javax.xml.bind.annotation.adapters.XmlAdapter;

public class MapAdapter extends XmlAdapter<MapWrapper, Map<String, String>> {
    
    @Override
    public Map<String, String> unmarshal(MapWrapper adaptedMap) throws Exception {
        Map<String, String> map = new TreeMap<>();
        adaptedMap.entry.forEach(entry -> {
            map.put(entry.name, entry.value);
        });
        return map;
    }

    @Override
    public MapWrapper marshal(Map<String, String> map) throws Exception {
        MapWrapper adaptedMap = new MapWrapper();
        map.entrySet().stream().map(mapEntry -> {
            Entry entry = new Entry();
            entry.name = mapEntry.getKey();
            entry.value = mapEntry.getValue();
            return entry;
        }).forEachOrdered(entry -> {
            adaptedMap.entry.add(entry);
        });
        return adaptedMap;
    }

}

在类Product中,您需要更改属性映射的注解:

@XmlElement(name="attrs")
@XmlJavaTypeAdapter(MapAdapter.class)
private Map<String, String> attributes;

这将产生以下输出:

<attrs>
    <a name="key">value</a>
</attrs>
英文:

For this purpose you'll need to use XmlAdapter.

Following beans should be created:

public class Entry {
@XmlAttribute
public String name;
@XmlValue
public String value;
}
public class MapWrapper {
@XmlElement(name=&quot;a&quot;)
public List&lt;Entry&gt; entry = new ArrayList&lt;&gt;();
}

XML Adapter:

import java.util.Map;
import java.util.TreeMap;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class MapAdapter extends XmlAdapter&lt;MapWrapper, Map&lt;String, String&gt;&gt; {
@Override
public Map&lt;String, String&gt; unmarshal(MapWrapper adaptedMap) throws Exception {
Map&lt;String, String&gt; map = new TreeMap&lt;&gt;();
adaptedMap.entry.forEach(entry -&gt; {
map.put(entry.key, entry.value);
});
return map;
}
@Override
public MapWrapper marshal(Map&lt;String, String&gt; map) throws Exception {
MapWrapper adaptedMap = new MapWrapper();
map.entrySet().stream().map(mapEntry -&gt; {
Entry entry = new Entry();
entry.key = mapEntry.getKey();
entry.value = mapEntry.getValue();
return entry;
}).forEachOrdered(entry -&gt; {
adaptedMap.entry.add(entry);
});
return adaptedMap;
}
}

In class Product you'll need to change annotations for attributes map:

@XmlElement(name=&quot;attrs&quot;)
@XmlJavaTypeAdapter(MapAdapter.class)
private Map&lt;String, String&gt; attributes;

That will give following output:

&lt;attrs&gt;
&lt;a name=&quot;key&quot;&gt;value&lt;/a&gt;
&lt;/attrs&gt;

huangapple
  • 本文由 发表于 2020年9月30日 17:12:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/64134435.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定