package er.rest.format; import java.io.StringReader; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.CDATASection; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Text; import org.xml.sax.InputSource; import er.rest.ERXRestContext; import er.rest.ERXRestNameRegistry; import er.rest.ERXRestRequestNode; import er.rest.ERXRestUtils; /** * ERXXmlRestRequestParser is an implementation of the IERXRestRequestParser interface that supports XML document * requests. * * @author mschrag */ public class ERXXmlRestParser implements IERXRestParser { protected ERXRestRequestNode createRequestNodeForElement(Element element, boolean rootNode, ERXRestFormat.Delegate delegate, ERXRestContext context) { String name = element.getTagName(); ERXRestRequestNode requestNode = new ERXRestRequestNode(name, rootNode); String valueStr = element.getNodeValue(); NamedNodeMap attributeNodes = element.getAttributes(); for (int attributeNum = 0; attributeNum < attributeNodes.getLength(); attributeNum++) { Node attribute = attributeNodes.item(attributeNum); requestNode.setAttributeForKey(attribute.getNodeValue(), attribute.getNodeName()); } NodeList childNodes = element.getChildNodes(); for (int childNodeNum = 0; childNodeNum < childNodes.getLength(); childNodeNum++) { Node childNode = childNodes.item(childNodeNum); if (childNode instanceof Element) { Element childElement = (Element) childNode; ERXRestRequestNode childRequestNode = createRequestNodeForElement(childElement, false, delegate, context); if (childRequestNode != null) { String childRequestNodeName = childRequestNode.name(); // MS: this is a huge hack, but it turns out that it's surprinsingly tricky to // identify an array in XML ... I'm cheating here and just saying that if the // node name is uppercase, it represents a new object and not an attribute ... // this will totally break on rails-style lowercase class names, but for now // this flag is just a heuristic if (childRequestNodeName == null || Character.isUpperCase(childRequestNodeName.charAt(0))) { childRequestNode.setRootNode(true); requestNode.setArray(true); } requestNode.addChild(childRequestNode); } } else if (childNode instanceof Text) { String text = childNode.getNodeValue(); if (text != null) { if (!(childNode instanceof CDATASection)) { text = text.trim(); } if (valueStr == null) { valueStr = text; } else { valueStr += text; } } // if there is a text node AND other sibling nodes, this is fake ... if (childNodes.getLength() > 1) { valueStr = null; } } else { // ??? } } requestNode.setValue(valueStr); delegate.nodeDidParse(requestNode); if (valueStr != null) { String type = requestNode.type(); if (type != null) { // MS: inverse the types we declare in ERXXmlRestWriter if ("datetime".equals(type)) { type = "NSTimestamp"; } if ("date".equals(type)) { type = "java.time.LocalDate"; } if ("datetime2".equals(type)) { type = "java.time.LocalDateTime"; } if ("time".equals(type)) { type = "java.time.LocalTime"; } else if ("integer".equals(type)) { type = "int"; } else if ("bigint".equals(type)) { type = "BigDecimal"; } else if ("enum".equals(type)) { type = "Enum"; } Object coercedValue = ERXRestUtils.coerceValueToTypeNamed(valueStr, type, context, false); if (coercedValue != null) { requestNode.setValue(coercedValue); } } } return requestNode; } @Override public ERXRestRequestNode parseRestRequest(IERXRestRequest request, ERXRestFormat.Delegate delegate, ERXRestContext context) { ERXRestRequestNode rootRequestNode = null; String contentString = request.stringContent(); if (contentString != null && contentString.length() > 0) { // MS: Support direct updating of primitive type keys -- so if you don't want to // wrap your request in XML, this will allow it if (!contentString.trim().startsWith("<")) { contentString = "<FakeWrapper>" + contentString.trim() + "</FakeWrapper>"; } Document document; try { document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(contentString))); document.normalize(); Element rootElement = document.getDocumentElement(); rootRequestNode = createRequestNodeForElement(rootElement, true, delegate, context); } catch (Exception e) { throw new IllegalArgumentException("Failed to parse request document.", e); } } else { rootRequestNode = new ERXRestRequestNode(null, true); rootRequestNode.setNull(true); } return rootRequestNode; } public static void main(String[] args) { String str = "<Company><id>100</id><type>Company</type><name>mDT</name><firstName nil=\"true\"/><employees><Employee id=\"101\" type=\"Employee\"/><Employee id=\"102\"><name>Mike</name></Employee></employees></Company>"; //String str = "<Employees><Employee id=\"101\" type=\"Employee\"/><Employee id=\"102\"><name>Mike</name></Employee></Employees>"; ERXRestNameRegistry.registry().setExternalNameForInternalName("Super", "Company"); ERXRestNameRegistry.registry().setExternalNameForInternalName("Super2", "Employee"); ERXRestContext context = new ERXRestContext(); ERXRestRequestNode n = new ERXXmlRestParser().parseRestRequest(new ERXStringRestRequest(str), new ERXRestFormatDelegate(), context); ERXStringBufferRestResponse response = new ERXStringBufferRestResponse(); new ERXXmlRestWriter().appendToResponse(n, response, new ERXRestFormatDelegate(), context); System.out.println("ERXXmlRestParser.main: " + response); } }