草庐IT

java - JAXB:我应该如何编码复杂的嵌套数据结构?

coder 2023-05-15 原文

我有几个复杂的数据结构,比如

Map< A, Set< B > >
Set< Map< A, B > >
Set< Map< A, Set< B > > >
Map< A, Map< B, Set< C > > >
and so on (more complex data structures)

注意:就我而言,使用 Set 还是 List 并不重要。

现在我知道 JAXB 让我定义 XmlAdapter 的,这很好, 但我不想为每个给定的数据结构定义一个 XmlAdapter (这将是太多的复制和粘贴代码)。

我试图通过声明两个泛化 XmlAdapter 来实现我的目标:

  • 一个用于 map :MapAdapter<K,V>
  • 一组:SetAdapter<V>

问题:
JAXB 提示如下:

javax.xml.bind.JAXBException:
class java.util.Collections$UnmodifiableMap nor any of its
  super class is known to this context.

这是我的适配器类:

import java.util.*;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.*;

public class Adapters {

public final static class MapAdapter<K, V>
        extends XmlAdapter<MapAdapter.Adapter<K, V>, Map<K, V>> {

    @XmlType
    @XmlRootElement
    public final static class Adapter<K, V> {

        @XmlElement
        protected List<MyEntry<K, V>> key = new LinkedList<MyEntry<K, V>>();

        private Adapter() {
        }

        public Adapter(Map<K, V> original) {
            for (Map.Entry<K, V> entry : original.entrySet()) {
                key.add(new MyEntry<K, V>(entry));
            }
        }

    }

    @XmlType
    @XmlRootElement
    public final static class MyEntry<K, V> {

        @XmlElement
        protected K key;

        @XmlElement
        protected V value;

        private MyEntry() {
        }

        public MyEntry(Map.Entry<K, V> original) {
            key = original.getKey();
            value = original.getValue();
        }

    }

    @Override
    public Adapter<K, V> marshal(Map<K, V> obj) {
        return new Adapter<K, V>(obj);
    }

    @Override
    public Map<K, V> unmarshal(Adapter<K, V> obj) {
        throw new UnsupportedOperationException("unmarshalling is never performed");
    }

}

}

这是我的 JUnit 测试用例:

import java.io.*;
import java.util.*;
import javax.xml.bind.*;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.*;
import org.junit.*;
import static java.lang.System.*;

public class SomeTest {

@Test
public void _map2()
        throws Exception {

    Map<String, Map<String, String>> dataStructure =
            new HashMap<String, Map<String, String>>();

    Map<String, String> inner1 = new HashMap<String, String>();
    Map<String, String> inner2 = new HashMap<String, String>();

    dataStructure.put("a", inner1);
    dataStructure.put("b", inner1);

    inner1.put("a1", "1");
    inner1.put("a2", "2");
    inner2.put("b1", "1");
    inner2.put("b2", "2");

    JAXBContext context = JAXBContext.newInstance(Adapters.XMap.class,
            Adapters.XCount.class, Adapters.XEntry.class);

    Marshaller marshaller = context.createMarshaller();
    marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

    marshaller.setAdapter(new Adapters.MapAdapter());

    StringWriter sw = new StringWriter();

    marshaller.marshal(dataStructure, sw);
    out.println(sw.toString());
}

}

最佳答案

我已经解决了没有 XmlAdapter 的问题

我已经为 MapMap.EntryCollection 编写了带有 JAXB 注释的对象。
主要思想在 xmlizeNestedStructure(...) 方法内部:

看一下代码:

public final class Adapters {

private Adapters() {
}

public static Class<?>[] getXmlClasses() {
    return new Class<?>[]{
                XMap.class, XEntry.class, XCollection.class, XCount.class
            };
}

public static Object xmlizeNestedStructure(Object input) {
    if (input instanceof Map<?, ?>) {
        return xmlizeNestedMap((Map<?, ?>) input);
    }
    if (input instanceof Collection<?>) {
        return xmlizeNestedCollection((Collection<?>) input);
    }

    return input; // non-special object, return as is
}

public static XMap<?, ?> xmlizeNestedMap(Map<?, ?> input) {
    XMap<Object, Object> ret = new XMap<Object, Object>();

    for (Map.Entry<?, ?> e : input.entrySet()) {
        ret.add(xmlizeNestedStructure(e.getKey()),
                xmlizeNestedStructure(e.getValue()));
    }

    return ret;
}

public static XCollection<?> xmlizeNestedCollection(Collection<?> input) {
    XCollection<Object> ret = new XCollection<Object>();

    for (Object entry : input) {
        ret.add(xmlizeNestedStructure(entry));
    }

    return ret;
}

@XmlType
@XmlRootElement
public final static class XMap<K, V> {

    @XmlElementWrapper(name = "map")
    @XmlElement(name = "entry")
    private List<XEntry<K, V>> list = new LinkedList<XEntry<K, V>>();

    public XMap() {
    }

    public void add(K key, V value) {
        list.add(new XEntry<K, V>(key, value));
    }

}

@XmlType
@XmlRootElement
public final static class XEntry<K, V> {

    @XmlElement
    private K key;

    @XmlElement
    private V value;

    private XEntry() {
    }

    public XEntry(K key, V value) {
        this.key = key;
        this.value = value;
    }

}

@XmlType
@XmlRootElement
public final static class XCollection<V> {

    @XmlElementWrapper(name = "list")
    @XmlElement(name = "entry")
    private List<V> list = new LinkedList<V>();

    public XCollection() {
    }

    public void add(V obj) {
        list.add(obj);
    }

}

}

有效!

让我们看一个演示输出:

<xMap>
    <map>
        <entry>
            <key xsi:type="xCount" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <count>1</count>
                <content xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">a</content>
            </key>
            <value xsi:type="xCollection" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <list>
                    <entry xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">a1</entry>
                    <entry xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">a2</entry>
                    <entry xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">a3</entry>
                </list>
            </value>
        </entry>
        <entry>
            <key xsi:type="xCount" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <count>2</count>
                <content xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">b</content>
            </key>
            <value xsi:type="xCollection" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <list>
                    <entry xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">b1</entry>
                    <entry xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">b3</entry>
                    <entry xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">b2</entry>
                </list>
            </value>
        </entry>
        <entry>
            <key xsi:type="xCount" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <count>3</count>
                <content xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">c</content>
            </key>
            <value xsi:type="xCollection" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <list>
                    <entry xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">c1</entry>
                    <entry xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">c2</entry>
                    <entry xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">c3</entry>
                </list>
            </value>
        </entry>
    </map>
</xMap>

抱歉,演示输出还使用了一个名为 "count" 的数据结构 适配器的源代码中没有提到。

顺便说一句:有谁知道如何消除所有这些烦人的 和(在我的情况下)不必要的 xsi:type 属性?

关于java - JAXB:我应该如何编码复杂的嵌套数据结构?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/818327/

有关java - JAXB:我应该如何编码复杂的嵌套数据结构?的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  4. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  5. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  6. ruby-on-rails - Rails 编辑表单不显示嵌套项 - 2

    我得到了一个包含嵌套链接的表单。编辑时链接字段为空的问题。这是我的表格:Editingkategori{:action=>'update',:id=>@konkurrancer.id})do|f|%>'Trackingurl',:style=>'width:500;'%>'Editkonkurrence'%>|我的konkurrencer模型:has_one:link我的链接模型:classLink我的konkurrancer编辑操作:defedit@konkurrancer=Konkurrancer.find(params[:id])@konkurrancer.link_attrib

  7. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  8. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  9. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  10. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

随机推荐