从 JSON Schema 动态创建 Swing GUI(使用 Metawidget)

正如标题所示,我想创建一个基于 JSON 模式(我实时获取)的 Swing GUI,并使用它来填充 JSONObject(Google SimpleJSON)。我正在考虑使用 Metawidget 框架来实现此目的,但到目前为止还没有成功。我在网上找到了各种参考资料,但似乎没有一个适用于这个特殊情况。示例中总是缺少一些类或方法,而且 Metawidget 的文档也不是很好(至少我无法找到 4.2 版本的一组示例)。我得到的 JSON Schema 描述了在服务器端使用 Jackson 的 JSONSchema 描述的 Java 类,但在本地不可用或可以提前知道,因此也应该处理这个问题。


有人对不同的方法有什么建议,或者我可以使用一些示例/参考吗?当然,使用 Metawidget 4.2 编译的具体代码也更受欢迎。


--- 编辑 --- (由于理查德·肯纳德的回应)


使用提供的代码块,我成功地生成了一个 GUI。但是,我需要修改“json”和“jsonSchema”字符串的值并插入其他值,并切换传递给 CompositeInspector 的检查器的顺序。这是代码和生成的 GUI:


final JFrame frame = new JFrame();

frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );


String json = "{\"person\": { \"firstname\": \"Richard\", \"surname\": \"Kennard\", \"notes\": \"Software developer\" }}";

String jsonSchema = "{ \"name\": \"person\", \"type\": \"person\", properties: { \"firstname\": { \"required\": true }, \"surname\": { \"required\": true }, \"notes\": { \"large\": true }}}";


final SwingMetawidget metawidget = new SwingMetawidget();

metawidget.setInspector( new CompositeInspector( new CompositeInspectorConfig().setInspectors(

        new JsonSchemaInspector( new JsonInspectorConfig().setInputStream( new ByteArrayInputStream( jsonSchema.getBytes() ) ) ),

        new JsonInspector( new JsonInspectorConfig().setInputStream( new ByteArrayInputStream( json.getBytes() ) ) )

)));


metawidget.setToInspect( json );

frame.add( metawidget, BorderLayout.CENTER );

frame.setSize(500, 500);

frame.setVisible(true);

https://img1.mukewang.com/64ccc591000169d404850192.jpg

这没有使用 MapWidgetProcessor,因为(我想)它需要修改以支持 String 到 JSONObject 的转换。(此外,该代码块中的“NAME”变量未定义,据说需要替换为“elementName”?)

然而,所有这些都引出了几个新问题:

1)为什么“json”中的值没有映射到组件?

2)如果我没有“json”值,而只有“jsonShema”值,应该如何设置?

3)为什么在模式中显式指定属性类型时代码不起作用,例如:

“名字”:{“必需”:true,“类型”:“字符串”}


紫衣仙女
浏览 94回答 2
2回答

德玛西亚99

Metawidget 的核心原则是允许您混合搭配各种方法以适合您的架构。所以我可以分片来回答这个问题。一个基本的 SwingMetawidget:// UIfinal JFrame frame = new JFrame();frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );// Metawidgetfinal SwingMetawidget metawidget = new SwingMetawidget();...configure Metawidget by setting inspectors, inspection result processors, widget builders, etc...metawidget.setToInspect( myData );frame.add( metawidget, BorderLayout.CENTER );要读取 JSON 类型数据和 JSON 架构,请使用 CompositeInspector:String json = "{ \"firstname\": \"Richard\", \"surname\": \"Kennard\", \"notes\": \"Software developer\" }";String jsonSchema = "{ properties: { \"firstname\": { \"required\": true }, \"notes\": { \"large\": true }}}";...metawidget.setInspector( new CompositeInspector( new CompositeInspectorConfig().setInspectors(&nbsp; &nbsp; &nbsp; new JsonInspector( new JsonInspectorConfig().setInputStream( new ByteArrayInputStream( json.getBytes() ) ) ),&nbsp; &nbsp; &nbsp; new JsonSchemaInspector( new JsonInspectorConfig().setInputStream( new ByteArrayInputStream( jsonSchema.getBytes() ) ) ) )要映射类型,请考虑添加 TypeMappingInspectionResultProcessor:metawidget.addInspectionResultProcessor(&nbsp; &nbsp; new TypeMappingInspectionResultProcessor<SwingMetawidget>(&nbsp; &nbsp; &nbsp; &nbsp; new TypeMappingInspectionResultProcessorConfig()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .setTypeMapping( "foo", "bar" )&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .setTypeMapping( "abc", "def" )));或者,可能是更好的方法,添加自定义 WidgetBuilder 来处理未知类型的小部件:metawidget.setWidgetBuilder( new CompositeWidetBuilder( new ompositeWidgetBuilderConfig()&nbsp; &nbsp; .setWidgetBuilders(&nbsp; &nbsp; &nbsp; &nbsp; new OverriddenWidgetBuilder(), new ReadOnlyWidgetBuilder(),&nbsp; &nbsp; &nbsp; &nbsp; new MyWidgetBuilder(), new SwingWidgetBuilder()&nbsp; &nbsp; )));MyWidgetBuilder 做类似的事情class MyWidgetBuilder&nbsp; &nbsp; implements WidgetBuilder<JComponent, SwingMetawidget> {&nbsp; &nbsp; public JComponent buildWidget( String elementName, Map<String, String> attributes, SwingMetawidget metawidget ) {&nbsp; &nbsp; &nbsp; &nbsp; if ( "my.special.type".equals( attributes.get( TYPE ) ) )&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return new JSuperWidget();&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; // Fall through to other WidgetBuilder&nbsp; &nbsp; &nbsp; &nbsp; return null;&nbsp; &nbsp; }默认情况下,JComponent 不会将其数据保存在任何地方。您需要为此添加类似 BeansBindingProcessor 的内容。当然,BeansBinding 只绑定到 JavaBean。如果你想绑定到其他东西(比如 JSON Map),你可以添加你自己的 MapWidgetProcessor:/*** MapWidgetProcessor uses the Metawidget's <code>toInspect</code> to retrieve/store values.*/public class MapWidgetProcessor&nbsp; &nbsp;implements AdvancedWidgetProcessor<JComponent, SwingMetawidget> {&nbsp; &nbsp;//&nbsp; &nbsp;// Public methods&nbsp; &nbsp;//&nbsp; &nbsp;@Override&nbsp; &nbsp;public void onStartBuild( SwingMetawidget metawidget ) {&nbsp; &nbsp; &nbsp; getWrittenComponents( metawidget ).clear();&nbsp; &nbsp;}&nbsp; &nbsp;/**&nbsp; &nbsp; * Retrieve the values from the Map and put them in the Components.&nbsp; &nbsp; */&nbsp; &nbsp;@Override&nbsp; &nbsp;public JComponent processWidget( JComponent component, String elementName, Map<String, String> attributes, SwingMetawidget metawidget ) {&nbsp; &nbsp; &nbsp; String attributeName = attributes.get( NAME );&nbsp; &nbsp; &nbsp; getWrittenComponents( metawidget ).put( attributeName, component );&nbsp; &nbsp; &nbsp; // Fetch the value...&nbsp; &nbsp; &nbsp; Map<String, Object> toInspect = metawidget.getToInspect();&nbsp; &nbsp; &nbsp; Object value = toInspect.get( attributeName );&nbsp; &nbsp; &nbsp; if ( value == null ) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return component;&nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; // ...and apply it to the component. For simplicity, we won't worry about converters&nbsp; &nbsp; &nbsp; String componentProperty = metawidget.getValueProperty( component );&nbsp; &nbsp; &nbsp; ClassUtils.setProperty( component, componentProperty, value );&nbsp; &nbsp; &nbsp; return component;&nbsp; &nbsp;}&nbsp; &nbsp;@Override&nbsp; &nbsp;public void onEndBuild( SwingMetawidget metawidget ) {&nbsp; &nbsp; &nbsp; // Do nothing&nbsp; &nbsp;}&nbsp; &nbsp;/**&nbsp; &nbsp; * Store the values from the Components back into the Map.&nbsp; &nbsp; */&nbsp; &nbsp;public void save( SwingMetawidget metawidget ) {&nbsp; &nbsp; &nbsp; Map<String, Object> toInspect = metawidget.getToInspect();&nbsp; &nbsp; &nbsp; for ( Map.Entry<String,JComponent> entry : getWrittenComponents( metawidget ).entrySet() ) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;JComponent component = entry.getValue();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;String componentProperty = metawidget.getValueProperty( component );&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Object value = ClassUtils.getProperty( component, componentProperty );&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;toInspect.put( entry.getKey(), value );&nbsp; &nbsp; &nbsp; }&nbsp; &nbsp;}&nbsp; &nbsp;//&nbsp; &nbsp;// Private methods&nbsp; &nbsp;//&nbsp; &nbsp;/**&nbsp; &nbsp; * During load-time we keep track of all the components. At save-time we write them all back&nbsp; &nbsp; * again.&nbsp; &nbsp; */&nbsp; &nbsp;private Map<String,JComponent> getWrittenComponents( SwingMetawidget metawidget ) {&nbsp; &nbsp; &nbsp; @SuppressWarnings( "unchecked" )&nbsp; &nbsp; &nbsp; Map<String,JComponent> writtenComponents = (Map<String,JComponent>) metawidget.getClientProperty( MapWidgetProcessor.class );&nbsp; &nbsp; &nbsp; if ( writtenComponents == null ) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;writtenComponents = CollectionUtils.newHashMap();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;metawidget.putClientProperty( MapWidgetProcessor.class, writtenComponents );&nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; return writtenComponents;&nbsp; &nbsp;}

MMTTMM

回答新问题:2)然后你必须提供完整的模式。目前 jsonSchema 仅具有“required”等属性。“type”等属性是从 json 对象值推断出来的。CompositeInspector 正在为您将它们合并在一起。但是如果你只想有一个 JsonSchemaInspector (没有 JsonInspector,没有 CompositeInspector),那么你的 jsonSchema 必须拥有所有属性3)因为“string”是JavaScript类型。Java 等效项是“java.lang.String”。因此,您可以使用 TypeMappingInspectionResultProcessor (或其子类 JsonSchemaMappingInspectionResultProcessor)。这可能看起来很繁重,但请记住您正在做的事情非常不寻常(在 Java 中渲染 JSON)。幸运的是,Metawidget 可以插入各种组合。最后:“支持 String 到 JSONObject 转换” - 我不这么认为。JSONObject 是顶层概念。它基本上是一张地图。您需要绑定的各个字段仍然是基元(字符串、数字等)。所以 MapWidgetProcessor 可能是一个不错的选择。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java