@SuppressWarnings({"unchecked", "rawtypes"}) // API design flaw prevents this from type-checking public void serveExposedBean(StaplerRequest req, Object exposedBean, Flavor flavor) throws ServletException, IOException { serveExposedBean(req, exposedBean, new ExportConfig().withFlavor(flavor).withPrettyPrint(req.hasParameter("pretty"))); }
@Override public void type(Type expected, Class actual) throws IOException { classAttr = config.getClassAttribute().print(expected, actual); }
JSONDataWriter(Writer out, ExportConfig config) throws IOException { this.out = out; this.config = config; indent = config.isPrettyPrint() ? 0 : -1; }
public abstract DataWriter createDataWriter(Object bean, Writer w, ExportConfig config) throws IOException; }
@Test(expected = IOException.class) public void testNotExportedBeanFailing() throws IOException { ExportConfig config = new ExportConfig().withFlavor(Flavor.JSON).withExportInterceptor(new ExportInterceptor2()).withSkipIfFail(true); StringWriter writer = new StringWriter(); ExportableBean b = new ExportableBean(); builder.get(ExportableBean.class).writeTo(b,Flavor.JSON.createDataWriter(b, writer, config)); }
default @Nonnull ExportConfig getExportConfig() { return new ExportConfig(); }
/** * Writes one property of the given object to {@link DataWriter}. * * @param pruner * Determines how to prune the object graph tree. */ @SuppressWarnings("unchecked") public void writeTo(Object object, TreePruner pruner, DataWriter writer) throws IOException { TreePruner child = pruner.accept(object, this); if (child==null) return; Object d = writer.getExportConfig().getExportInterceptor().getValue(this,object, writer.getExportConfig()); if ((d==null && skipNull) || d == ExportInterceptor.SKIP) { // don't write anything return; } if (merge) { // merged property will get all its properties written here if (d != null) { Class<?> objectType = d.getClass(); Model model = owner.getOrNull(objectType, parent.type, name); if (model == null && !writer.getExportConfig().isSkipIfFail()) { throw new NotExportableException(objectType); } else if (model != null) { model.writeNestedObjectTo(d, new FilteringTreePruner(parent.HAS_PROPERTY_NAME_IN_ANCESTRY,child), writer); } } } else { writer.name(name); writeValue(type, d, child, writer); } }
@Override public Object getValue(Property property, Object model, ExportConfig config) throws IOException { try { return property.getValue(model); } catch (IllegalAccessException | InvocationTargetException e) { if(config.isSkipIfFail()) { LOGGER.log(Level.WARNING,"Failed to get \"" + property.name + "\" from a " + model.getClass().getName(), e); return SKIP; } throw new IOException("Failed to write " + property.name + ":" + e.getMessage(), e); } } };
/** * Serves the exposed bean in the specified flavor. * * <p> * This method performs the complete output from the header to the response body. * If the flavor is JSON, this method also supports JSONP via the {@code jsonp} query parameter. * * <p>The {@code depth} parameter may be used to specify a recursion depth * as in {@link Model#writeTo(Object,int,DataWriter)} * * <p>As of 1.146, the {@code tree} parameter may be used to control the output * in detail; see {@link NamedPathPruner#NamedPathPruner(String)} for details. * * <p> {@link ExportConfig} is passed by the caller to control serialization behavior * @since 1.251 */ default void serveExposedBean(StaplerRequest req, Object exposedBean, ExportConfig exportConfig) throws ServletException,IOException { serveExposedBean(req, exposedBean, exportConfig.getFlavor()); }
@Test public void testNotExportedBean() throws IOException { ExportConfig config = new ExportConfig().withFlavor(Flavor.JSON).withExportInterceptor(new ExportInterceptor1()).withSkipIfFail(true); StringWriter writer = new StringWriter(); ExportableBean b = new ExportableBean(); builder.get(ExportableBean.class).writeTo(b,Flavor.JSON.createDataWriter(b, writer, config)); Assert.assertEquals("{\"_class\":\""+ExportableBean.class.getName()+"\",\"name\":\"property1\",\"notExportedBean\":{},\"shouldBeNull\":null}", writer.toString()); }
public abstract DataWriter createDataWriter(Object bean, Writer w, ExportConfig config) throws IOException; }
default @Nonnull ExportConfig getExportConfig() { return new ExportConfig(); }
/** * Writes one property of the given object to {@link DataWriter}. * * @param pruner * Determines how to prune the object graph tree. */ @SuppressWarnings("unchecked") public void writeTo(Object object, TreePruner pruner, DataWriter writer) throws IOException { TreePruner child = pruner.accept(object, this); if (child==null) return; Object d = writer.getExportConfig().getExportInterceptor().getValue(this,object, writer.getExportConfig()); if ((d==null && skipNull) || d == ExportInterceptor.SKIP) { // don't write anything return; } if (merge) { // merged property will get all its properties written here if (d != null) { Class<?> objectType = d.getClass(); Model model = owner.getOrNull(objectType, parent.type, name); if (model == null && !writer.getExportConfig().isSkipIfFail()) { throw new NotExportableException(objectType); } else if (model != null) { model.writeNestedObjectTo(d, new FilteringTreePruner(parent.HAS_PROPERTY_NAME_IN_ANCESTRY,child), writer); } } } else { writer.name(name); writeValue(type, d, child, writer); } }
@Override public Object getValue(Property property, Object model, ExportConfig config) throws IOException { try { return property.getValue(model); } catch (IllegalAccessException | InvocationTargetException e) { if(config.isSkipIfFail()) { LOGGER.log(Level.WARNING,"Failed to get \"" + property.name + "\" from a " + model.getClass().getName(), e); return SKIP; } throw new IOException("Failed to write " + property.name + ":" + e.getMessage(), e); } } };
/** * Serves the exposed bean in the specified flavor. * * <p> * This method performs the complete output from the header to the response body. * If the flavor is JSON, this method also supports JSONP via the {@code jsonp} query parameter. * * <p>The {@code depth} parameter may be used to specify a recursion depth * as in {@link Model#writeTo(Object,int,DataWriter)} * * <p>As of 1.146, the {@code tree} parameter may be used to control the output * in detail; see {@link NamedPathPruner#NamedPathPruner(String)} for details. * * <p> {@link ExportConfig} is passed by the caller to control serialization behavior * @since 1.251 */ default void serveExposedBean(StaplerRequest req, Object exposedBean, ExportConfig exportConfig) throws ServletException,IOException { serveExposedBean(req, exposedBean, exportConfig.getFlavor()); }
@SuppressWarnings({"unchecked", "rawtypes"}) // API design flaw prevents this from type-checking public void serveExposedBean(StaplerRequest req, Object exposedBean, Flavor flavor) throws ServletException, IOException { serveExposedBean(req, exposedBean, new ExportConfig().withFlavor(flavor).withPrettyPrint(req.hasParameter("pretty"))); }
/** * Writes one value of the property to {@link DataWriter}. */ private void writeValue(Type expected, Object value, TreePruner pruner, DataWriter writer) throws IOException { writeValue(expected,value,pruner,writer,writer.getExportConfig().isSkipIfFail()); }
@Override public void type(Type expected, Class actual) throws IOException { classAttr = config.getClassAttribute().print(expected, actual); }
@Override public void serveExposedBean(StaplerRequest req, Object exposedBean, ExportConfig config) throws ServletException, IOException { String pad=null; Flavor flavor = config.getFlavor(); setContentType(flavor.contentType); Writer w = getCompressedWriter(req);
JSONDataWriter(Writer out, ExportConfig config) throws IOException { this.out = out; this.config = config; indent = config.isPrettyPrint() ? 0 : -1; }