I'm writing an application that outputs xml and I'm using a jsp to do it (in eclipse). It works fine, but the jsp editor complains about the xml tags. Is there a way to convince the jsp editor to validate xml instead of html?
Oh, I misread that entirely.
You can turn of JSP format / verification in Eclipse properties.
In addition ensure you have <%# page contentType="application/rss+xml" %> in your JSP.
Previous answer.
1) Create an XML util class that has methods to abstract the generation of XML, auto-handle pretty-print, etc. I could see it having public methods like:
public void init();
// <tagname> and <tagname />
public void openTag(String tagname, boolean close);
// <tagname x="y">
public void openTag(String tagname, Map<String, String> attributes, boolean close);
// </tagname>
public void closeTag(String tagname);
// <tagname>value</tagname>
public void valueTag(String tagname, String value);
public void valueTag(String tagname, Map<String, String> attributes, String value);
public String getXML();
or you could have each operation return a String and manage the StringBuilder yourself.
2) Why are you generating XML in a JSP class, which should be used for presentation?
Related
my code
#GetMapping(value = {"/metadata"}, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.OK)
#ResponseBody
public String getMetadata() {
return dppService.getMetadata();
}
the method getMetadata will just return a json string. it just read data from the json file, and it is in another library can not be changed.
But when call this api, i got the follow reponse:
"{\"Namespace\":\"com.xxx\"...
the json string was escaped.
expected:
"{"Namespace":"com.xxx"...
How could i make it return the right json? BTW, our other services also return a json string in the controller, but their response will not be escaped which is so confused for me.
You could do this two ways:
From what I could understand you are having this issues because you might be returning the json as a string from from the service method dppService.getMetadata() by converting it manually to a string. If so , change that and instead return a POJO class from the service method as well as the controller, spring default jackson converter should automatically convert it to a json when the request is served. (I would suggest you go with this approach)
Another approach (the hacky less desirable one) if you still want to keep returning a string then you could configure the StringMessageConverter like below to accept json:
#Override
public void configureMessageConverters(
List<HttpMessageConverter<?>> converters) {
StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(
Charset.forName("UTF-8"));
stringConverter.setSupportedMediaTypes(Arrays.asList( //
MediaType.TEXT_PLAIN, //
MediaType.TEXT_HTML, //
MediaType.APPLICATION_JSON));
converters.add(stringConverter);
}
root cause:
There is a configuration file in the project:
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MappingJackson2HttpMessageConverter(jacksonBuilder().build()));
converters.stream()
.filter(converter -> converter instanceof MappingJackson2HttpMessageConverter)
.findFirst()
.ifPresent(converter -> ((MappingJackson2HttpMessageConverter) converter).setDefaultCharset(UTF_8));
}
This configuration overrite the defualt jackson behavior. There are two ways to solve this issue:
1.Remove this configuration, then it will be the default behavior
2.Add the StringHttpMessageConverter in this configuration, see Ananthapadmanabhan's option2
I am trying to create my own Html helper that will allow me to reuse some functionality across any web application. If I wanted to reuse this control in a single wep app I could create a .cshtml file and call it via the Html.Partial("") method and pass in a Model.
However as I have a class library project for my custom Html helpers I am creating the html with a string builder like this simplified version
StringBuilder htmlBuilder = new StringBuilder("<div class='myClass'>")
foreach(var item in MyItems)
{
htmlBuilder.Append($"item : {item.Name}");
}
htmlBuilder.append("</div>");
This makes it a pain to maintain especially as my control gets more features.
Is there a recommended way to leverage the razor engine where I can write the html in a .cshtml file with a model and then generate the html instead of using a string builder?
Yes. You can use a templated HTML helper to separate your view (HTML elements) from your model.
However, the downside is that you generally must put the templates either in the /Views/Shared/DisplayTemplates folder or a /DisplayTemplates folder inside of the view folder that represents the current controller. In the latter case, you can only use the template inside of that specific folder. It is possible to make a custom view engine that will pull the default templates as resources of a DLL file - see the MvcSiteMapProvider project for an example view engine implementation.
Example Templated HTML Helper
public class MyHelperModel
{
public string Title { get; set; }
public string Body { get; set; }
}
// Extension Methods for HTML helper
public static class MyHelperExtensions
{
public static MvcHtmlString MyHelper(this HtmlHelper helper, string title, string body)
{
return MyHelper(helper, title, body, null);
}
public static MvcHtmlString MyHelper(this HtmlHelper helper, string title, string body, string templateName)
{
// Build the model
var model = BuildModel(title, body);
// Create the HTML helper for the model
return CreateHtmlHelperForModel(helper, model)
.DisplayFor(m => model, templateName);
}
private static MyHelperModel BuildModel(string title, string body)
{
// Map to model
return new MyHelperModel
{
Title = title,
Body = body
};
}
private static HtmlHelper<TModel> CreateHtmlHelperForModel<TModel>(this HtmlHelper helper, TModel model)
{
return new HtmlHelper<TModel>(helper.ViewContext, new ViewDataContainer<TModel>(model));
}
}
public class ViewDataContainer<TModel>
: IViewDataContainer
{
public ViewDataContainer(TModel model)
{
ViewData = new ViewDataDictionary<TModel>(model);
}
public ViewDataDictionary ViewData { get; set; }
}
MyHelperModel.cshtml
The default conventions use a display template with the same name as the model when no templateName argument is passed (or it is null). Therefore, this will be our default HTML helper format. Note that you could instead just hard-code the HTML elements into the helper in the default case instead of using a template (or going the extra mile of creating a view engine).
As mentioned above, this should be in the /Views/Shared/DisplayTemplates/ folder, but you could make a custom view engine to pull the default template from a DLL.
#model MyHelperModel
<h3>#Model.Title</h3>
<p>#Model.Body</p>
CustomHtmlHelperTemplate.cshtml
Here is a named template that can be used within the application to change the HTML elements applied to the HTML helper.
As mentioned above, this should be in the /Views/Shared/DisplayTemplates/ folder, but you could make a custom view engine to pull the default template from a DLL.
#model MyHelperModel
<h1>#Model.Title</h1>
<p><i>#Model.Body</i></p>
Usage
#Html.MyHelper(
"This is the default template",
"This is what happens when we don't pass a template name to the HTML helper.")
#Html.MyHelper(
"This is a custom template",
"This is a custom template with different HTML elements than the default template.",
"CustomHtmlHelperTemplate")
NOTE: To ensure the helpers are available in the views, you need to add the namespaces in the /Views/Web.config file at <system.web.webPages.razor><pages><namespaces>.
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Optimization" />
<add namespace="System.Web.Routing" />
<!-- Add your namespaces here -->
<add namespace="MyProject.HtmlHelperNamespace" />
<add namespace="MyProject.HtmlHelperNamespace.Models" />
</namespaces>
</pages>
</system.web.webPages.razor>
You can maintain and generate at runtime the output of a Razor View / PartialView (cshtml), using this code:
public static string GetViewPageHtml(Controller controller, object model, string viewName)
{
ViewEngineResult result = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
if (result.View == null)
throw new Exception(string.Format("View Page {0} was not found", viewName));
controller.ViewData.Model = model;
StringBuilder sb = new StringBuilder();
using (StringWriter sw = new StringWriter(sb))
{
using (System.Web.UI.HtmlTextWriter output = new System.Web.UI.HtmlTextWriter(sw))
{
ViewContext viewContext = new ViewContext(controller.ControllerContext, result.View, controller.ViewData, controller.TempData, output);
result.View.Render(viewContext, output);
}
}
return sb.ToString();
}
You call it like this (from a Controller)
string result = GetViewPageHtml(this, viewModel, "~/Views/Home/Index.cshtml");
I’m using JBoss 7.1.3.As.Final and am building a Spring 3.2.11.RELEASE web application. On my JSP page I have this
${jsonString}
and what I would like to know is assuming this json string has an attribute “name”, how do I use JSTL (preferably no scriptlets) to print out the value associated with the “name” attribute to my page? Unsurprisingly, this doesn’t work
${jsonString[‘name’]}
If you can use third party libraries (e.g. Jackson), it should be fairly simple to accomplish this functionality. You will still have to create some java files to make this work though. First, create a POJO that matches your json data (in your case Employee could be something else but your POJO should match your fields).
public class Employee{
private String name;
private int age;
private String company;
public String getName(){
return name;
}
public String getCompany(){
return company;
}
public Integer getAge(){
return age;
}
//implement setters
}
Next, create a list wrapper for Employee class like this
public class EmployeeList {
public List<Employee> employees=new ArrayList<Employee>();
}
Now, create a JsonParser class (add Jackson library to the app classpath as well your app lib folder)
import org.codehaus.jackson.map.ObjectMapper;
public class JsonParser {
ObjectMapper objectMapper=new ObjectMapper();
public <T> T parseJson(String json,Class<T> targetType)throws Exception{
//uncomment this line if you want to ignore some fields, they dont have to be in your POJO then
//objectMapper.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return objectMapper.readValue(json, targetType);
}
}
Note that JsonParser can handle any type not just Employee. Now, in your jsp
add the following import (add jstl-1.2.jar to your classpath as well as your app lib folder)
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
Add the following code to the body section
<body>
<%# page import="java.util.List"%>
<%# page import="apps.simpleweb.json.JsonParser"%>
<%# page import="apps.simpleweb.data.Employee"%>
<%# page import="apps.simpleweb.data.EmployeeList"%>
<%
//example json string ; note that employees matches the list name
String jsonString = "{ \"employees\": [ {\"name\": \"Peter\", \"age\": 25, \"company\": \"XXXX\" },{ \"name\": \"Mark\", \"age\":45, \"company\": \"XXXX\"} ] }";
JsonParser parser = new JsonParser();
EmployeeList empList = parser.parseJson(jsonString, EmployeeList.class);
request.setAttribute("employeeList", empList.employees);
%>
<c:forEach items="${employeeList}" var="employee" >
<c:out value="Name : ${employee.name}" />
<c:out value="Age : ${employee.age}" />
</c:forEach>
You should be able to avoid scriptlet code entirely if you move the parsing to the servlet.
This question is for Mr.Blaise Doughan on JAXB Namespace
I have situation where,
Have a sample.xsd (old version - no namespace ). Generated JAXB classes using the XJC for the same XSD file. I got one example that uses the JAXB classes to unmarshal the XML data file , based on the XSD.
The sample.xsd file got changed (new version - added namespace). Again generated JAXB classes using the XJC for the new XSD file. The Example is updated so that it can now work for new XSD file
Now I got a situation , where iam getting XML data file based on old XSD and I would like to use the updated example file to unmarshal the old XML data.
One solution I could see , generating two object factory one with namespace and one without namespace. Can we do that? if so I can use the appropriate Object factory based on the my XML data I get.
Or would like to know , how can I generate JAXB classes for both XSD files , but XJC is not generating , it shows error - No changes detected in schema or binding files - skipping source generation.
Can I create a wrapper over the new Object Factory so that it can handle both ?
Please do provide me with some solution so that I can unmarshal the old file with new JAXB classes. Can
Apply a Namespace
In the case where the input XML does not have a namespace you can leverage a SAX XMLFilter to apply a namespace.
import org.xml.sax.*;
import org.xml.sax.helpers.XMLFilterImpl;
public class NamespaceFilter extends XMLFilterImpl {
private static final String NAMESPACE = "http://www.example.com/customer";
#Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
super.endElement(NAMESPACE, localName, qName);
}
#Override
public void startElement(String uri, String localName, String qName,
Attributes atts) throws SAXException {
super.startElement(NAMESPACE, localName, qName, atts);
}
}
Do the Unmarshal
The unmarshalling is done leveraging JAXB's UnmarshallerHandler as the ContentHandler
import javax.xml.bind.*;
import javax.xml.parsers.*;
import org.xml.sax.*;
public class Demo {
public static void main(String[] args) throws Exception {
// Create the JAXBContext
JAXBContext jc = JAXBContext.newInstance(Customer.class);
// Create the XMLFilter
XMLFilter filter = new NamespaceFilter();
// Set the parent XMLReader on the XMLFilter
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
filter.setParent(xr);
// Set UnmarshallerHandler as ContentHandler on XMLFilter
Unmarshaller unmarshaller = jc.createUnmarshaller();
UnmarshallerHandler unmarshallerHandler = unmarshaller
.getUnmarshallerHandler();
filter.setContentHandler(unmarshallerHandler);
// Parse the XML
InputSource xml = new InputSource("src/blog/namespace/sax/input.xml");
filter.parse(xml);
Customer customer = (Customer) unmarshallerHandler.getResult();
// Marshal the Customer object back to XML
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(customer, System.out);
}
}
For More Information
http://blog.bdoughan.com/2012/11/applying-namespace-during-jaxb-unmarshal.html
Ive been wanting to create a struts 2 with json return type using the annotation configuration. Ive successfully created this using the xml-type configuration like this snippet:
<action name="FetchJSON" class="com.stikiflem.Json" method="getJSON">
<result type="json"/>
</action>
I have posted a working demo of using an xml-type config here
http://stikiflem.wordpress.com/2008/08/27/struts-2-json-sample/
But how do I convert this to annotation? Here is my sample class:
public class JsonAction extends ActionSupport{
private List sampleList;
public String execute() {
sampleList = new ArrayList();
sampleList.add("stikiflem sample 1");
sampleList.add("stikiflem sample 2");
sampleList.add("stikiflem sample 3");
sampleList.add("stikiflem sample 4");
System.out.println("----------------------------------------------");
System.out.println("----------------------------------------------");
System.out.println("-sample111List:" + sampleList.toString());
System.out.println("----------------------------------------------");
System.out.println("----------------------------------------------");
return SUCCESS;
}
#Action(value="FetchJSON", results = {
#Result(name="success", type="json")
})
public String getJSON(){
System.out.println("get jason ko");
return execute();
}
public List getSampleList() {
return sampleList;
}
public void setSampleList(List sampleList) {
this.sampleList = sampleList;
}
}
Tried calling it by "json.action", it triggers the execute() method of course but cannot return a json type. Calling it by "FetchJSON" doesnt do anything. This question sounds stupid but there are just a small amount of tutorials and example of a detailed annotation in the net. Ive read a Manning Struts 2 in action book but it just barely scratch the surface, just the typical hello world and sucess,input redirection.
Ive searched the net high and low and so far, i havent seen any. I know there are a lot of programmers searching for this too.Hope someone can enlighten me about this one. Ive been banging my head on this for days already. :(
A similar question was asked here:
Struts2 JSON Plugin With Annotations
I got your action working by annotating it as follows:
#ParentPackage("json-default")
#Result(name="success", type="json")
public class JsonAction extends ActionSupport {
Get the JAR Dependencies
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-convention-plugin</artifactId>
<version>2.3.20</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-json-plugin</artifactId>
<version>2.3.20</version>
</dependency>
Convention Plugin
The Convention Plugin is bundled with Struts since 2.1 and replaces the Codebehind Plugin and Zero Config plugins. It provides the following features :
Action location by package naming conventions
Result (JSP, FreeMarker, etc) location by naming conventions
Class name to URL naming convention
Package name to namespace convention
Action name overrides using annotations
Namespace overrides using annotations
XWork package overrides using annotations
Set Parent Package
Using annotation set the package as json-default to support the JSON.
#ParentPackage("json-default")
Set Result Type
#Result(name="success", type="json")
Define filter in web.xml
Define the struts 2 filter in web.xml and pass the action class by defining actionPackages.
Action Class
In this class data converted into JSON format.
#Result(name = "success", type = "json")
#ParentPackage("json-default")
public class StrutsJsonAnnotationAction extends ActionSupport {
private static final long serialVersionUID = 3516335522937177571L;
private String name = "Narendra Modi";
private String designation = "Prime Minister of India";
private String dob = "17 September 1950";
private String[] education = {"MA", "BA"};
private List<String> favBooks = new ArrayList<String>();
private Map<String, String> assumedOffice = new HashMap<String, String>();
public StrutsJsonAnnotationAction() {
favBooks.add("Ramayan");
favBooks.add("Geeta");
assumedOffice.put("President", "Pranab Mukherjee");
assumedOffice.put("Preceded by", "Manmohan Singh");
}
#org.apache.struts2.convention.annotation.Action("/india")
#Override
public String execute() {
return SUCCESS;
}
Source:
http://www.websparrow.org/struts/struts2-and-json-integration-using-annotation-example