001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.marshall.json5l; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020 021import org.apache.juneau.commons.collections.*; 022import org.apache.juneau.marshall.json5.*; 023 024/** 025 * Parses JSON5L (JSON5 Lines) input into POJO models. 026 * 027 * <h5 class='topic'>Media types</h5> 028 * <p> 029 * Handles <c>Content-Type</c> types: <bc>application/json5l, application/json5lines, text/json5l</bc> 030 * 031 * <h5 class='topic'>Description</h5> 032 * <p> 033 * JSON5L combines the relaxed JSON5 dialect with JSONL's newline-delimited framing. Each non-empty, 034 * non-comment line is parsed as a complete JSON5 value (single-quoted strings, unquoted field names, 035 * trailing commas, comments, relaxed numbers). Because JSON5 is a strict superset of JSON, this 036 * parser also reads plain JSONL input unchanged. 037 * 038 * <p> 039 * Comment handling follows JSON5 line-by-line: a line that is blank or contains only a comment is 040 * skipped (like a blank line); inline comments within a record's JSON are tolerated. A 041 * <c>/* */</c> block comment must open and close on the same physical line — block comments 042 * cannot span record boundaries. 043 * 044 * <h5 class='section'>Example:</h5> 045 * <p class='bjava'> 046 * <jc>// Parse JSON5L into a list of POJOs</jc> 047 * List<MyBean> <jv>list</jv> = Json5lParser.<jsf>DEFAULT</jsf>.read(<jv>json5lInput</jv>, List.<jk>class</jk>, MyBean.<jk>class</jk>); 048 * </p> 049 * 050 * <h5 class='figure'>Example input:</h5> 051 * <p class='bjson'> 052 * // header comment line 053 * {name:'Alice',age:30} 054 * {name:'Bob',age:25} // trailing comment 055 * </p> 056 * 057 * <h5 class='section'>Notes:</h5><ul> 058 * <li class='note'>This class is thread safe and reusable. 059 * </ul> 060 * 061 * <h5 class='section'>See Also:</h5><ul> 062 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/Json5l">JSON5L Basics</a> 063 * </ul> 064 */ 065@SuppressWarnings({ 066 "java:S110", // Inheritance depth acceptable for Json5lParser hierarchy 067 "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants 068 "java:S9149" // Per-format static factories intentionally shadow the parent's. 069}) 070public class Json5lParser extends Json5Parser { 071 072 /** 073 * Builder class. 074 */ 075 public static class Builder extends Json5Parser.Builder { 076 077 private static final Cache<HashKey,Json5lParser> CACHE = Cache.of(HashKey.class, Json5lParser.class).build(); 078 079 /** 080 * Constructor, default settings. 081 */ 082 protected Builder() { 083 consumes("application/json5l,application/json5lines,text/json5l,application/jsonl,application/jsonlines,application/x-ndjson,text/jsonl") 084 .type(Json5lParser.class); 085 } 086 087 /** 088 * Copy constructor. 089 * 090 * @param copyFrom The builder to copy from. 091 * <br>Cannot be <jk>null</jk>. 092 */ 093 protected Builder(Builder copyFrom) { 094 super(reqnn("copyFrom", copyFrom)); 095 } 096 097 /** 098 * Copy constructor. 099 * 100 * @param copyFrom The bean to copy from. 101 * <br>Cannot be <jk>null</jk>. 102 */ 103 protected Builder(Json5lParser copyFrom) { 104 super(reqnn("copyFrom", copyFrom)); 105 } 106 107 @Override /* Overridden from Context.Builder<?> */ 108 public Json5lParser build() { 109 return cache(CACHE).build(Json5lParser.class); 110 } 111 112 @Override /* Overridden from Context.Builder<?> */ 113 public Builder copy() { 114 return new Builder(this); 115 } 116 } 117 118 /** Default parser, Accept=application/json5l. */ 119 public static final Json5lParser DEFAULT = new Json5lParser(create()); 120 121 /** 122 * Creates a new builder for this object. 123 * 124 * @return A new builder. 125 */ 126 public static Builder create() { 127 return new Builder(); 128 } 129 130 /** 131 * Constructor. 132 * 133 * @param builder The builder for this object. 134 */ 135 public Json5lParser(Builder builder) { 136 super(builder); 137 } 138 139 @Override /* Overridden from Context */ 140 public Builder copy() { 141 return new Builder(this); 142 } 143 144 @Override /* Overridden from Context */ 145 public Json5lParserSession.Builder createSession() { 146 return Json5lParserSession.create(this); 147 } 148 149 @Override /* Overridden from Context */ 150 public Json5lParserSession getSession() { 151 return createSession().build(); 152 } 153}